Editor's Note: This file was selected as MATLAB Central Pick of the Week
FDEP dissects MATLAB files and iteratively looks for all user defined functions (modules), which are used during runtime
FDEP retrieves for each module its
- subfunctions
- nested functions
- anonymous functions
- eval class calls
- unresolved calls
and all
- ML stock functions
- ML built-in functions
- ML classes
- ML toolboxes
that it uses
runtime options and returned macros create user-friendly,
intuitively comprehensible, and interactive GUIs, which
- list the results in various panels and listboxes
- plot a full synopsis for each module with
exhaustive information
- plot a full dependency matrix
- plot a runtime and modules tree
in essence, FDEP is a wrapper for DEPFUN and MLINT; however, due to an efficient pruning engine it is considerably (20-100 times!) faster
FDEP is particularly useful if you want to distribute your functions and need so see what else to include as well as the toolboxes they use
FDEP may be useful to debug an erroneous function with correct syntax
see
> help fdep
and the accompanying published M-file for help and an exhaustive example
us (2021). fdep: a pedestrian function dependencies finder (https://www.mathworks.com/matlabcentral/fileexchange/17291-fdep-a-pedestrian-function-dependencies-finder), MATLAB Central File Exchange. Retrieved .
Inspired by: detab: a pedestrian string detabulator, farg: a pedestrian M-file parser showing all used functions' syntax
Inspired: ftoc (v1.2), GraphViz-like tools for MATLAB, dep (unmaintained), plot_subfun, plot_depfun
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!Create scripts with code, output, and formatted text in a single executable document.
After additional testing, I made some improvements and bugfixes to the code fixes in my previous post. With these fixes, fdep is almost completely working as normal, minus listing of the full path of Matlab class dependencies and a couple of other minor items described in the comments.
Also, with the below fixes applied to fdep.m, the GraphViz-like tools for MATLAB works correctly with fdep after making one minor bug fix to mGraphVix.m.
Comparing files .\DOWNLOADS\fdep.m and .\SOURCE\REPOS\FDEP\FDEP.M
***** .\DOWNLOADS\fdep.m
117: dopt={
118: '-toponly'
119: '-quiet'
120: };
***** .\SOURCE\REPOS\FDEP\FDEP.M
117: dopt={
118: 'toponly'
119: };
*****
***** .\DOWNLOADS\fdep.m
761:
762: [dtmp,dmod,dmcl,docx,docx,docx,docx,docl]=depfun(fnam,par.dopt{:}); %#ok
763: im=strncmp(par.mlroot,dtmp,numel(par.mlroot));
***** .\SOURCE\REPOS\FDEP\FDEP.M
760:
761: compLevel0OnlyOpt = true;
762: if ~any(ismember(par.dopt,'toponly')); compLevel0OnlyOpt = false; end
763:
764: comp = matlab.depfun.internal.Completion({fnam},matlab.depfun.internal.Target.parse('None'),compLevel0OnlyOpt);
765: dtmp = {parts(comp).path}'; % This also initializes the BuiltinListMap.
766:
767: % Subsequent code expects the main function path to be 1st in
768: % the list. If the main function is not first, then fix the list
769: if ~strcmpi(dtmp{1},fnam)
770: dtmp(strcmpi(dtmp,fnam))=[];
771: dtmp = [fnam;dtmp];
772: end
773:
774: builtIns = {};
775: if retrieveBuiltinListMap(comp).Count > 0
776: builtIns = subsref(values(retrieveBuiltinListMap(comp)),substruct('{}',{1}));
777: end
778:
779: dmod = {};
780: dmcl = {};
781:
782: if ~isempty(builtIns)
783: dmod = cellfun(@(x)x.Symbol,builtIns,'UniformOutput',false)';
784: dmcl = cellfun(@(x)matlab.depfun.internal.MatlabSymbol.basename(x.QSymbol),builtIns,'UniformOutput',false)';
785: ixbic = ismember(dmcl,matlab.depfun.internal.requirementsConstants.matlabBuiltinClasses);
786: dmcl(~ixbic) = []; % keep only members of the built-in class names
787: end
788:
789: depgraph = getDependencyGraph(comp);
790:
791: if depgraph.VertexCount > 1
792: dmcladd = cell(depgraph.VertexCount,1);
793: j=0;
794: for i = 0:depgraph.VertexCount-1
795: if depgraph.vertex(i).Data.symbol.Type == matlab.depfun.internal.MatlabType.BuiltinClass
796: j=j+1;
797: dmcladd{j} = depgraph.vertex(i).Data.symbol.Symbol;
798: end
799: end
800: if j < depgraph.VertexCount
801: dmcladd(j+1:end)=[];
802: end
803: dmcl = [dmcl;dmcladd];
804: end
805:
806: dmcl = unique(dmcl); % Partially re-implemented Matlab class listing. Complete re-implementation would include full
807: path resolution.
808:
809: docx = cell(size(dtmp)); % Not yet re-implemented. Same length as dtmp. If first entry is the main function, the mai
810: n function is marked as recursive. This is the only use of the 'docx' information.
811: docl = {}; % Not yet re-implemented. This is only used to identify classes of external types, e.g. Java, .NET.
812:
813: im=strncmp(par.mlroot,dtmp,numel(par.mlroot));
*****
***** .\DOWNLOADS\fdep.m
776:
777: dtmp{1}=fap.wnam;
778: if fap.mp(1)
***** .\SOURCE\REPOS\FDEP\FDEP.M
826:
827: dtmp{1}=fap.wnam; % First path must be to current fun
828: if fap.mp(1)
*****
***** .\DOWNLOADS\fdep.m
2672: par.rexcmp='(?<='').*(?='')|(?<=(\s))\d+(?=(\.)$)';
2673: par.rexcyc='The McCabe complexity of';
2674: par.rexeva='(^feval$)|(^evalc$)|(^evalin$)|(^eval$)';
***** .\SOURCE\REPOS\FDEP\FDEP.M
2722: par.rexcmp='(?<='').*(?='')|(?<=(\s))\d+(?=(\.)$)';
2723: par.rexcyc='The McCabe '; % For a script, this reads The McCabe cyclomatic complexity... For a function, this
2724: reads The McCabe complexity of '[function name]'...
2725: par.rexeva='(^feval$)|(^evalc$)|(^evalin$)|(^eval$)';
*****
***** .\DOWNLOADS\fdep.m
2890: if any(ix)
2891: cyc=regexp(cyc(ix),par.rexcmp,'match');
2892: cyc=[cyc{:}]';
***** .\SOURCE\REPOS\FDEP\FDEP.M
2941: if any(ix)
2942: cyc=regexp(cyc(ix),par.rexcmp,'match');
2943: cyc=cellfun(@(x)fill_empty_fcnname_with_mfilename(x,par.wnam),cyc,'UniformOutput',false);
2944: cyc=[cyc{:}]';
*****
***** .\DOWNLOADS\fdep.m
2936: end
2937: function [p,par]=FARG_get_calls(p,par)
***** .\SOURCE\REPOS\FDEP\FDEP.M
2988: end
2989:
2990: function infoCell = fill_empty_fcnname_with_mfilename(infoCell,filepath)
2991: if length(infoCell)==1
2992: infoCell = {'',infoCell{1}};
2993: [~, infoCell{1}, ~]=fileparts(filepath);
2994: end
2995: end
2996:
2997: function [p,par]=FARG_get_calls(p,par)
*****
***** .\DOWNLOADS\fdep.m
3238: if any(ix) % FUNCTION
3239: nfmt(ix)=par.lint.cyc(:,2);
3240: else % SCRIPT
***** .\SOURCE\REPOS\FDEP\FDEP.M
3298: if any(ix) % FUNCTION
3299: nfmt(ix)=par.lint.cyc(ix,2);
3300: else % SCRIPT
*****
Although depfun was removed, the internal Matlab dependencies that enable depfun to work have not been removed. Making the below changes will render this tool mostly functional (minus Java class dependencies and 'called from' info). Note that these fixes have not been extensively tested.
Comparing files .\DOWNLOADS\fdep.m and .\SOURCE\REPOS\FDEP\FDEP.M
***** .\DOWNLOADS\fdep.m
117: dopt={
118: '-toponly'
119: '-quiet'
120: };
***** .\SOURCE\REPOS\FDEP\FDEP.M
117: dopt={
118: 'toponly'
119: };
*****
***** .\DOWNLOADS\fdep.m
761:
762: [dtmp,dmod,dmcl,docx,docx,docx,docx,docl]=depfun(fnam,par.dopt{:}); %#ok
763: im=strncmp(par.mlroot,dtmp,numel(par.mlroot));
***** .\SOURCE\REPOS\FDEP\FDEP.M
760:
761: compLevel0OnlyOpt = true;
762: if ~any(ismember(par.dopt,'toponly')); compLevel0OnlyOpt = false; end
763:
764: comp = matlab.depfun.internal.Completion({fnam},matlab.depfun.internal.Target.parse('MATLAB'),compLevel0OnlyOpt);
765: dtmp = {parts(comp).path}'; % This also initializes the BuiltinListMap
766:
767: builtIns = {};
768: if retrieveBuiltinListMap(comp).Count > 0
769: builtIns = subsref(values(retrieveBuiltinListMap(comp)),substruct('{}',{1}));
770: end
771: dmod = {};
772: dmcl = {};
773:
774: if ~isempty(builtIns)
775: dmod = cellfun(@(x)x.Symbol,builtIns,'UniformOutput',false)';
776: dmcl = knownClasses(builtIns{1}.classList)';
777: end
778:
779: docx = {}; % Not yet re-implemented
780: docl = {}; % Not yet re-implemented
781:
782: im=strncmp(par.mlroot,dtmp,numel(par.mlroot));
*****
***** .\DOWNLOADS\fdep.m
814: end
815: if ~isempty(docx{1})
816: par.spec(tenum.R)=1; % spec: R
***** .\SOURCE\REPOS\FDEP\FDEP.M
833: end
834: if ~isempty(docx) && ~isempty(docx{1})
835: par.spec(tenum.R)=1; % spec: R
*****
***** .\DOWNLOADS\fdep.m
2672: par.rexcmp='(?<='').*(?='')|(?<=(\s))\d+(?=(\.)$)';
2673: par.rexcyc='The McCabe complexity of';
2674: par.rexeva='(^feval$)|(^evalc$)|(^evalin$)|(^eval$)';
***** .\SOURCE\REPOS\FDEP\FDEP.M
2691: par.rexcmp='(?<='').*(?='')|(?<=(\s))\d+(?=(\.)$)';
2692: par.rexcyc='The McCabe '; % For a script, this reads The McCabe cyclomatic complexity... For a function, this
2693: reads The McCabe complexity of '[function name]'...
2694: par.rexeva='(^feval$)|(^evalc$)|(^evalin$)|(^eval$)';
*****
***** .\DOWNLOADS\fdep.m
2890: if any(ix)
2891: cyc=regexp(cyc(ix),par.rexcmp,'match');
2892: cyc=[cyc{:}]';
***** .\SOURCE\REPOS\FDEP\FDEP.M
2910: if any(ix)
2911: cyc=regexp(cyc(ix),par.rexcmp,'match');
2912: cyc=cellfun(@(x)fill_empty_fcnname_with_mfilename(x,par.wnam),cyc,'UniformOutput',false);
2913: cyc=[cyc{:}]';
*****
***** .\DOWNLOADS\fdep.m
2936: end
2937: function [p,par]=FARG_get_calls(p,par)
***** .\SOURCE\REPOS\FDEP\FDEP.M
2957: end
2958:
2959: function infoCell = fill_empty_fcnname_with_mfilename(infoCell,filepath)
2960: if length(infoCell)==1
2961: infoCell = {'',infoCell{1}};
2962: [~, infoCell{1}, ~]=fileparts(filepath);
2963: end
2964: end
2965:
2966: function [p,par]=FARG_get_calls(p,par)
*****
***** .\DOWNLOADS\fdep.m
3238: if any(ix) % FUNCTION
3239: nfmt(ix)=par.lint.cyc(:,2);
3240: else % SCRIPT
***** .\SOURCE\REPOS\FDEP\FDEP.M
3267: if any(ix) % FUNCTION
3268: nfmt(ix)=par.lint.cyc(ix,2);
3269: else % SCRIPT
*****
Should be withdrawn to avoid time wasting.
Should be withdrawn to avoid time wasting.
I am sorry to be the bearer of bad news. The DEPFUN function (required for FDEP) was removed from MATLAB in v2016a.
Note to developers: Please change "release compatibility" to MATLAB versions prior to v2016a. It looks like it would have been a very useful program for me..
R2019B Error
R2017b ERROR
Could you tell me how to fix the depfun calls in details please?
I tried replacing the depfun call with matlab.codetools.requiredFilesAndProducts.However, it is still not working!
This needs to be updated for R2014b+ by replacing depfun calls with matlab.codetools.requiredFilesAndProducts.
Would be great if someone makes a fix for the latest matlab. Now it throws an error about depfun() usage.
Incredible, had I known of the existence of this tool five years ago and I would have saved many headaches.
Thank you
Clayton Chu,
Yep, but "just replace function name" approach is not working.
R2015a still only shows a warning, but R2015b already throws an error
:(
This needs to be updated for R2014b+ by replacing depfun calls with matlab.codetools.requiredFilesAndProducts.
Great. Many thanks!
Why didn't I find this before? A wonderful time-saving utility.
Good Lord this function is amazing! Bravo!
Very useful tool!
However, in the latest version 20 Jun 2010, the maximum allowed m-file-name for class files (scripts and functions are ok) is 40 characters! For longer file names, fdep will produce an obscure error message:
In an assignment A(:) = B, the number of elements in A and B must be the same.
It took me some time to figure this out... Unfortunately I have no solution other than to use shorter class names at the moment. This does not seem to be related to depfun.
Any suggestions?
much faster than depfun, like 20 times. thanks.
but it does not seem to return .fig files.
(also problematic file)
This is a great tool !
However, I need to also get the position in the file (line & column) of each function call. Is it possible ? If yes, could you advise me how to do it ?
Hi us,
fdep brings an error on schema.m file used in UDD Packages.
Thanks for the great contrib!
Amazing! The fact that it also list the real toolboxes that are used is precious
This is a phenomenal tool! I've been using depfun and this is a much better tool. Thank you...
Fantastic tool! It helped me locate the function that required a unlicensed toolbox.
There is one thing I've noticed I want to share:
fdep() and the underlaying depfun() does not interprete "constant-conditional" branches, i.e. when you have
if 1
Foo();
else
Bla();
end
it lists both functions. In my case, this led to the case that MATLAB complained about a unlicensed Toolbox - which I did not use. Commenting out Bla() resolved the problem. :)
Very useful tool, and fast!
It worked great for me, except for one issue: if the input function calls subfun.m only from within an anonymous function, then fdep() does not find it.
In other words if myfun.m has the lines
Sfunc=@(x)subfun(x);
x=rand;
y=Sfunc(x);
then for p=fdep('myfun'), p.fun does NOT contain subfun.m
This came up because I tend to use anonymous functions as aliases a lot, e.g. Fab=@(c,d)F(a,b,c,d)
WOW! brilliant and fast. Highly recommend! Thank you
exactly what I needed
very helpful and fast.
Great function. Saves lots of time and headache. Much faster than depfun which I had to force quit to stop after 20 minutes.
This is an excellent function that I've used for years. However, the GUIs don't play nicely with multiple monitors. Because of the mix of normalized and absolute units used for various parts of the GUI, simply resizing the whole thing post-render isn't ideal (and I just discovered by accident that there are some buttons in the upper corner that I never knew existed in all my time using it). It would be nice if I could somehow tell it which portion of the screen to use (even if I had to hardcode that in my own version, but right now that seems to require changing several lines throughout the code, and I haven't tracked them all down).
WoW ! which a great tool!
Excellent tool, much faster and more informative than DEPFUN.
An especially useful feature is detection of unresolved function calls, which DEPFUN doesn't seem to do. The synopses of each module have a list of unresolved names, though I couldn't see a way to get a single list of all unresolved names except by browsing.
Also you might want to switch off latex interpretation in the point labels in the dependency matrix (line 1921, I think).
Good work!
Hi, I am having some problems using fdep on a script/function that calls functions which are part of a collection.
e.g. fdep('myfunction', '-m')
where
function [x] = myFunction(y)
x = mycollection.getX(y);
end
Is there a switch to capture this?
Thanks
Jon
This is awesome! My job requires using/debugging code written by someone else and this is extremely useful. Thank you for easy to navigate, clean, well written code :)
Amazing!
(Continuation of my previous error report). In case it helps, after tracing the error I reported earlier, I noticed the following:
The error occurs at:
ixb(1:par.mfun,1)=find(ixt(:,1)==1);
(??? Subscripted assignment dimension mismatch.)
and this is because
K>> par.mfun
ans =
32
K>> numel(find(ixt(:,1) == 1))
ans =
41
I'm getting the following error when running fdep on a function that uses IBM's CPLEX
26 7 : | cplexbilp P 1: C:\ILOG\CPLEX_Studio_AcademicResearch122\cplex\matlab\x64_win64\cplexcreateprob.p
27 8 : | cplexcreateprob R P 1: C:\ILOG\CPLEX_Studio_AcademicResearch122\cplex\matlab\x64_win64\cplexoptimset.p
28 9 : | cplexoptimset P 1: C:\ILOG\CPLEX_Studio_AcademicResearch122\cplex\matlab\x64_win64\@Cplex\Cplex.p
??? Subscripted assignment dimension mismatch.
Error in ==> fdep>FARG_get_entries at 3066
ixb(1:par.mfun,1)=find(ixt(:,1)==1);
Error in ==> fdep>farg at 2585
[p,par]=FARG_get_entries(p,par);
Error in ==> fdep>FDEP_get_fun at 770
[fa,fap]=farg(fnam,'-s','-d');
Error in ==> fdep>FDEP_get_dep at 664
[p,par,dtmp,dmlf,dmod,dmcl,docl]=FDEP_get_fun(p,par,fnam,frot);
Error in ==> fdep>FDEP_get_dep at 740
[p,par]=FDEP_get_dep(p,par,dtmp{i});
Any ideas why this happens?
Very Useful. Would it be possible to handle also comments of, e.g.
%ToDo-things?
Splendid! I am really impressed with this tool, especially, as others have commented, the speed up over depfun. Do you know if it will work with the new(ish) user classdefs?
Ok, I have been damn stupid. Sorry. This piece of software is excellent!
Thanks a lot, generally it works great! Unfortunately I have to analyze a gui-based software project and fdep seems only to consider automatically called functions. E.g. submodules which are called due to user input are not analyzed. Is that correct?
And if yes, what can I do about it? I really need to find out, which toolboxes are involved in a rather confusing project I recently "inherited".
andrew
unfortunately: no...
one of the reasons why the .EVAL. group is collected separately by fdep and a warning flag is displayed if any such construct is found...
which, in turn, is one of the reasons why one should follow these rules
- do NOT use EVAL
- use FEVAL with function handles, only
note:
feval(@sind,30)
will correctly find the call to SIND...
sorry
us
Is fdep capable of finding feval calls, where the function name argument in feval is itself a string variable?
e.g.
feval(fname,1,2,3)
thomas
thanks for your comment
to achieve what you need, you could do this
pp=fdep('yourmain');
% now,
pp.sub.M
% is an array listing all the modules with their full path
% you could simply create a little utility, which does the copy, eg
function copymod(pp,newfolder)
% error checking etc left out...
for i=1:pp.nfun
copyfile(pp.sub(i).M,newfolder);
end
end
us
This is fantastic. It worked quickly on a matlab program that contains 184 functions. An additional capability that would be useful would be to have an option in fdep to copy all the non-toolbox dependencies to the current working directory, although I think I could write this myself from the information provided in the P structure. Thanks us.
I really find useful this tool.
oleg
thanks for your comment
there are two reasons
1) a MODULE is a M-file/-script, and the first (main) function
is simply another (sub-) function within this file
2) the first function should(!) have the same
name as the M-file (otherwise, mlint complains); this is a simple
sanity check for the user
also, note the first char in the panel
M (...) ... % <- the main function
S (...) ... % <- the first (official) subfunction
S (...) ... % <- and so on
us
The package is great but I don't understand why the main function is listed in the subfunction pane (or in the 'sub.S.fn'):
function foo
end
function foo2
end
I'd expect only foo2 to be there. What is the rationale?
Tnx
Excellent function!
In Windows, the file separators in output.froot point the wrong way, though. Is that on purpose?
hi dirk
great suggestion for an enhancement!...
i built a helper function, which does what you need and will email it to you if you want...
it will become part of FDEP as soon as i can get to it...
us
Thanks for this fantastic tool. The speed-up compared to depfun is amazing.
Though there is one feature I am missing somewhat:
fdep lets you identify which toolboxes are used from _somewhere_ in you code easiliy. But unfortunately it is not so easy to identify which of my .m files caused this toolbox dependency.
What would be great would be a possibilty to, e.g. select one of the toolboxes in the list-view which in turn could highlights all modules that directly depend on the selected tool box.
jason
yes, this is by design...
while it could easily be changed, it also (potentially) would lead to undesirable results...
thus, to FDEP a file residing in a folder, which is not in ML's path, you must CD into that folder...
however, let me know if this is a real problem for you, as it certainly can be changed...
us
For being as full-featured as it appears, why will this function not accept full path names?
fdep('C:\blah\blah\blah\file.m')
I get an error saying "file not found". It only seems to work with files in current directory or in the MATLAB path.
I liked the previous version very much! Thanks a lot.
The latest version has a bug. If a sealed class (e.g. with a definition "classdef (Sealed) MyClass") is analyzed, the program crashes in fdep on line 2928. If "(Sealed)" is removed the program works as advertised.
with the latest version (08-Jun-2009 22:24:13/R14-R2009a), per isakson's error messages do not show anymore...
the reason was an old parser engine, which did not handle more recent function constructs...
thanks, per, for alerting me to this problem...
us
This is a great idea, implemented in a great way!
Full of user-fraindly and usefull features.
Great job.
very nice. thank you for sharing this very useful too
Great function! Very useful. Is it possible to see also other files required in a package, like mat, txt, ini, etc?
I just downloaded it and it looks very nice. However I'd like you to fix the bug where it's locating the wrong module. I have a main module and it calls 8 other modules, 7 of which are in the same folder as the main module. However, module #2 you list as being one of the same name that I happen to have in another folder. I'm not sure why you're seeing that one first - MATLAB doesn't, it sees and runs the proper one which is in the same module as the main module. For example, in folder1, I have m-files myFile and mySubFile1, mySubFile2, etc. But in folder2, there is also a mySubFile1 and this is the one that your program is finding instead of the mySubFile1 in folder1.
A few more suggestions on the list GUI:
1. Replace the [John D'] button with a scroll bar or two small buttons to increase and decrease the font size.
2. I'd also like to see tooltips on your 6 buttons (right off the bat, it's not obvious what some of them do).
3. I'd like it to start with a GUI rather than a 2-steps command line process.
4. The Image Processing Toolbox is incorrectly called "images" in the toolboxes listbox.
5. A longer and more descriptive name than the short and cryptic "fdep" would be nice. And what does "pedestrian" have to do with it? Is that word needed?
Here's what MATLAB reports for my toolboxes:
MATLAB Version 7.7 (R2008b)
Image Processing Toolbox Version 6.2 (R2008b)
MATLAB Builder NE Version 3.0 (R2008b)
MATLAB Compiler Version 4.9 (R2008b)
Signal Processing Toolbox Version 6.10 (R2008b)
ToolBox EXAMPLE_SCRIPTS Version 1.0.0
Just my 2 cents on a well done program.
andrew, can you get in touch with me?unfortunately, you didn't provide an email address.
It falls over when mex files are used.
to accommodate FDEP to JD's eyesight, an upgrade was just uploaded with these changes
1) candy colors were moved towards zero (still hoping that they group the various panels quickly for the eye...)
2) a JOHN D' button was added; its help section says:
utility in honor of John D'errico, a senior and most respected
FEX and CSSM contributor, with very poor eyesight
CLICKING on the button will grow the fontsize by 1 point every time
the version tag is 07-Nov-2007 21:10:00
us
As always, a fully featured, fully able tool from us. Well done.
I do have one comment - my older eyes had a moderately hard time with the tiny font size and color scheme in the resulting gui. Pastel colored text on a pastel background was hard to read. Time for a new prescription in my glasses?
Extremely helpful, indeed! Also, the author did very quickly add a new feature after a pleasant email exchange.
wow!