Skip to content

Instantly share code, notes, and snippets.

@TommasoBelluzzo
Last active March 8, 2020 03:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TommasoBelluzzo/3e4100a92027d7b504171232fb1ac377 to your computer and use it in GitHub Desktop.
Save TommasoBelluzzo/3e4100a92027d7b504171232fb1ac377 to your computer and use it in GitHub Desktop.
A script for detecting dependancies and minimum required version of other Matlab scripts.
% [INPUT]
% path = A string representing the path on which to perform the requirements check (optional, default=pwd).
% It can be either the path to a Matlab script or the path to a directory containing one or more Matlab scripts at any level of depth.
% type = A string representing the requirements check to perform (optional, default=ALL), its value can be one of the following:
% - ALL: dependancies and minimum version.
% - DEP: dependancies only.
% - VER: minimum version only.
% extv = A boolean indicating whether to return detailed information concerning the minimum version (optional, default=false).
% If false, the result is returned in the form of a string representing the minimum version.
% Otherwise, the result is returned in the form of a table listing all the called functions and their respective minimum version (if available).
% This parameter should be specified only when the minimum version check must be performed.
%
% [OUTPUT]
% out = The result of the requirements check, whose content depends upon "type" and "extv" input arguments:
% - ALL: a structure with two fields, "Deps" and "VerInfo", whose values are described below.
% - DEP: a cell array of strings that represent the name of the dependancies.
% - VER: if "extv" is false, a string representing the minimum version, otherwise an n-by-3 table listing all the called functions and their respective minimum version (if available).
function out = check_requirements(varargin)
persistent p;
if (isempty(p))
p = inputParser();
p.addOptional('path','',@(x)validateattributes(x,{'char','string'},{'scalartext','nonempty'}));
p.addOptional('type','ALL',@(x)any(validatestring(x,{'ALL','DEP','VER'})));
p.addOptional('extv',false,@(x)validateattributes(x,{'logical'},{'scalar'}));
end
p.parse(varargin{:});
res = p.Results;
path = strtrim(char(res.path));
type = res.type;
extv = res.extv;
if ((nargin == 3) && strcmp(type,'DEP'))
error('The ''extv'' parameter should be specified only when the minimum version check must be performed.');
end
if (isempty(path))
path = pwd();
end
search = exist(path); %#ok<EXIST>
files = [];
if (search == 2)
[~,~,ext] = fileparts(path);
if (strcmpi(ext,'.m'))
files = {path};
end
elseif (search == 7)
files = find_scripts(path);
end
if (isempty(files))
error('No scripts to analyse have been found.');
end
out = check_requirements_internal(files,type,extv);
end
function out = check_requirements_internal(files,type,extv)
persistent dict;
if (isempty(dict))
dict = containers.Map('KeyType','char','ValueType','any');
end
files_len = numel(files);
if (any(strcmp(type,{'ALL' 'DEP'})))
deps = cell(files_len,1);
for i = 1:files_len
[~,p] = matlab.codetools.requiredFilesAndProducts(files{i});
deps{i} = {p.Name};
end
deps = unique([deps{:}]);
end
if (any(strcmp(type,{'ALL' 'VER'})))
funs = cell(files_len,1);
for i = 1:files_len
ci = getcallinfo(files{i},'flat');
funs{i} = [ci.calls.atCalls.names ci.calls.dotCalls.names ci.calls.fcnCalls.names];
end
funs = unique([funs{:}]).';
funs_len = numel(funs);
vers = table(funs,NaN(funs_len,1),repmat({''},funs_len,1),'VariableNames',{'Function' 'Major' 'Minor'});
funs = lower(funs);
for i = 1:funs_len
fun = funs{i};
if (isKey(dict,fun))
vers(i,[2 3]) = dict(fun);
continue;
end
html = download_page(['http://mathworks.com/help/matlab/ref/' fun '.html']);
if (isempty(html))
html = download_page(['http://mathworks.com/help/simulink/slref/' fun '.html']);
end
if (isempty(html))
continue;
end
tokens = regexp(html,'Introduced (?:before|in) R([0-9]+)(a|b)','tokens');
if (numel(tokens) ~= 1)
continue;
end
tokens = [tokens{:}];
values = {str2double(tokens{1}) tokens{2}};
dict(fun) = values;
vers(i,[2 3]) = values;
end
vers_ko = isnan(vers.Major);
vers_clean = vers;
vers_clean(vers_ko,:) = [];
vers_clean = sortrows(vers_clean,[2 3],{'descend' 'descend'});
if (isempty(vers_clean))
if (extv)
ver_info = vers_clean;
else
ver_info = 'Unknown';
end
else
if (extv)
ver_info = [vers_clean; vers(vers_ko,:)];
else
ver_info = ['R' num2str(vers_clean{1,2}) char(vers_clean{1,3})];
end
end
end
switch (type)
case 'DEP'
out = deps;
case 'VER'
out = ver_info;
otherwise
out = struct();
out.Deps = deps;
out.VerInfo = ver_info;
end
end
function html = download_page(url)
if (verLessThan('Matlab','8.4'))
[html,status] = urlread(url); %#ok<URLRD>
if (status == 0)
html = '';
end
else
try
html = webread(url);
catch
html = '';
end
end
end
function files = find_scripts(folder)
pattern = [folder filesep() '*.m'];
f = ls(pattern);
if (isempty(f))
files = {};
else
files = strcat(folder,filesep(),cellstr(f));
end
subfolders = cellstr(ls(folder));
subfolders(1:2) = [];
subfolders(~cellfun(@isdir,strcat(folder,filesep(),subfolders))) = [];
subfolders_len = numel(subfolders);
subfiles = cell(subfolders_len,1);
for i = 1:subfolders_len
subfiles{i} = find_scripts([folder filesep() subfolders{i}]);
end
files = [files; vertcat(subfiles{:})];
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment