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