3

有没有办法确定 .m 文件的所有依赖关系以及它使用脚本中的命令(命令行)调用的文件的任何依赖关系?

之前有一个这样的问题,真的很好,因为它建议使用该depfun功能。但问题在于它正在输出它所依赖的MATLAB相关文件。

示例:testing.m

disp('TESTING!!');

depfun('testing') 的输出

'C:\testing.m'
'C:\MATLAB\R2008a\toolbox\matlab\datatypes\@opaque\char.m'
'C:\MATLAB\R2008a\toolbox\matlab\datatypes\@opaque\double.m'
'C:\MATLAB\R2008a\toolbox\matlab\datatypes\@opaque\toChar.m'
'C:\MATLAB\R2008a\toolbox\matlab\elfun\log10.m'
'C:\MATLAB\R2008a\toolbox\matlab\elmat\ans.m'

等等

名单有点长。

这里的重点是我希望有一些类似的功能或标志可以删除这些不需要的依赖项。

4

7 回答 7

2

当我编写一个简单的函数来为 m 文件创建目录时,我发现以下几个链接很有帮助:

  • 讨论未记录函数MLINTMEX的线程
  • Urs Schwarz 在 MathWorks 文件交换上的FDEP
  • FARG by Urs Schwarz 在 MathWorks 文件交换上

编辑:由于这个问题激起了我的好奇心,我开始尝试一些可能接近它的方法。查找对非工具箱 .m 和 .mex 文件的依赖关系相对简单(我在 MATLAB 版本 7.1.0.246 中执行此操作):

fcnName = 'myfile.m';
fcnList = depfun(fcnName,'-quiet');
listIndex = strmatch('C:\Program Files\MATLAB71\toolbox',fcnList);
fcnList = fcnList(setdiff(1:numel(fcnList),listIndex));

在这里,我只是使用DEPFUN来获取依赖项,然后我删除了所有以“C:\Program Files\MATLAB71\toolbox”开头的文件,MATLAB 工具箱位于我的机器上。请注意,这假设您没有将任何自己的代码放在这些 MATLAB 目录中(无论如何您都不应该这样做)。

为了获得对 .mat 和 .txt 文件的依赖,我采用了另一种方法。对于从上述代码中获得的每个文件,您可以将文件的文本加载到 MATLAB 中并使用正则表达式对其进行解析,以查找以“.mat”或“.txt”结尾的字符串:

fid = fopen(fcnName,'rt');
fcnText = fscanf(fid,'%c');
fclose(fid);
expr = '[^\'']\''([^\''\n\r]+(?:\w\.(?:mat|txt)){1})\''[^\'']';
dataFiles = regexp(fcnText,expr,'tokens');
dataFiles = unique([dataFiles{:}]).';

我使用的正则表达式有一些限制:

  • 如果你有一个像'help.txt'这样的字符串出现在一个注释中(比如一个函数的帮助注释块),它仍然会被正则表达式检测到。我尝试使用环视运算符解决此问题,但运行时间太长。

  • 如果您从变量构建字符串(例如“fileString = [someString '.mat']”),则正则表达式不会检测到它。

  • 返回的文件名字符串将是相对路径字符串。换句话说,如果函数中有字符串 'help.txt' 或 'C:\temp\junk.mat',正则表达式匹配将返回 'help.txt' 或 'C:\temp\junk.mat ',正如它们在函数中出现的那样。要查找完整路径,您可以对每个数据文件使用WHICH函数(假设文件位于 MATLAB 路径的某个位置)。

希望你觉得这些有用!=)

于 2009-02-26T22:58:31.613 回答
1

试试TMW FileExchange的 DepSubFun。

于 2009-02-26T22:47:57.147 回答
0

感谢您到目前为止的回复。

我不认为这些都是我想要完成的。

我希望已经有一些东西可以确定在主 m 文件中调用的本地函数,将它们添加到列表中,然后继续查看每个函数,直到没有剩余。似乎这些解决方案中的任何一个都没有这样做

我想出了一个我将尝试实施的计划。这可能有点蛮力,设计可能会随着我的工作而改变,但这里是概念。

在这个初始设计中有很多假设,但由于它主要是针对我和其他一些人的,所以我认为这对我的一般解决方案来说不是一个大问题。

要查找的文件类型:.m .mat .mex* .txt(将根据需要更新)

确定 matlabpath 并清除工具箱路径(这是假设您的工作目录不称为工具箱或您没有添加到其他工具箱的任何特殊 m 文件的地方)

希望只留下您使用的目录并且可以从中调用函数。(还假设您没有硬编码某种类型的 [run 'C:\random\myscript.m']

蛮力部分:查找您感兴趣的文件类型,并列出您的工作目录(pwd)中的文件类型和剩余的 matlab 路径

删除与工作目录中匹配的文件名。

遍历搜索每个文件名的主 m 文件,如果找到,将其添加到依赖文件数组中。从原始列表中删除依赖文件。使用“新”原始列表搜索依赖文件列表,重复直到没有文件或根本没有匹配。

到目前为止,这只是我的概念,我也会搜索更多。

于 2009-02-27T16:15:01.667 回答
0

我今天终于运行了这个脚本,它是一个基于 Windows matlab 的脚本,因为它调用了 '!findstr "something" file.txt'。(我更喜欢 grep 但不知道 matlab 等价物。

我要问我的老板是否允许我将其发布在 matlab 文件交换上以与他人分享,所以希望我能很快用链接更新它。

gnovice:我没有足够的代表来评论 gnovice 对我在编写代码之前写的描述的评论。

但基本上要确定它的作用是获取所有文件的文件名(分为文件类型类别),剥离完整路径名和扩展名,使用上面提到的 !findstr 命令在您正在构建的 .m 文件中搜索它依赖并将其输出到 temp.txt 文件(这是因为我无法找到一种方法来在命令的输出上获得 1 或 0 或 isempty 返回)

这是我个人搜索以确定是否使用每个文件的细分:

.m : 'filename ' 或 'filename(' % 涵盖了 'filename (' case .mex* : 与上面相同 .mat : 与上面相同,但将更改为某种负载和 'filename.mat ' 明天可能会处理这个 .txt :只需搜索 'filename.txt'

使用这种方法,您最终可能会得到一些额外的文本文件或 .m 文件,但这里的关键是您至少应该拥有所需的所有文件。

它还递归地在所有依赖文件上调用自身,以便它们的依赖关系也被考虑在内。

-迟迟

于 2009-03-04T03:41:14.997 回答
0

我很久以前写过代码来为八度音程做这个。我主要使用它来为graphviz生成.dot文件来可视化依赖关系,但我也在makefile中使用它来在编译代码时包装依赖关系。不幸的是,它是 perl 代码,但您可以通过 shell 调用它来从脚本运行它。它是完全递归的。

要运行它,您必须将 OCT_BASE 更改为指向代码的根目录。(对不起,它不是 matlab 的路径变量感知)。那么我可能会将它作为 perl octavedepgrapher.pl -l 运行


#! /bin/sh
exec perl -x -S $0 ${1+"$@"} # -*-perl-*-
#!perl
#
# octavedepgrapher.pl
# find the dependancy graph of octave file(s). prints a 
# dot file suitable for graphviz
# Author: steven e. pav
# Created: 2006.07.16
# SVN: $Id$
#
# * Thu Aug 30 2007 Steven Pav 
# - expanding to recognize matlabs pragma of %#function funcname
# version 0.3   2007.04.17
#  add raw output mode.
# version 0.2   2007.03.05
#  add media selection
# version 0.1   2006.08.24
#  fixed multiple functions within file.
#  added multiple edgeout capability.
#  adding clusters for files.
# version 0.0   2006.07.16
#  created.
#
#
########################################################################

########################################
# change only this
########################################

#@OCT_BASE = qw(/home/spav/sys/octave/m/ ./ $ENV{OCTAVE});
@OCT_BASE = qw(/home/spav/sys/octave/m/ ./);


########################################################################

$VERSION = "octavedepgrapher    version 0.02   2006.08.23\n";

########################################################################

use Getopt::Long;
$Getopt::Long::ignorecase = 0;
$Getopt::Long::order = $PERMUTE;

%OPT_MEANINGS = (
                 'H' => 'show Help.',
                 'l' => 'list the dependencies to standard out. do not make a dot file.',
                 'p' => 'give full path names.',
                 'm' => 'multi-edge. one for each function call.',
                 'g' => 'map connections from functions to global variables.',
                 'G' => 'map connections between functions which share global variables.',
                 'C' => 'do not cluster files.',
                 'D' => 'Debug.',
                 'd=s' => 'dependency mode for makefiles.  sets -p and -l, and but outputs in makefile suitable format. the string is the extension (with dot) to substitute for .m',
                 'r=s' => 'aspect ratio (can be fill, auto, compact (default))',
                 'B=s' => 'base directory. if given, all directories are assumed relative to this one.',
                 'L=s' => 'colon separated list of base directories of libraries (_overrides_ OCT_BASE). should probably include ./',
                 'l=s' => 'colon separated list of base directories of libraries (in addition to OCT_BASE).',
                 'X=s' => 'colon separated list of base directories to exclude in the search.',
                 'M=s' => 'media selection',
                 );

$OPTS = join('',(map { substr($_,0,1); } keys(%OPT_MEANINGS)));

&GetOptions(keys %OPT_MEANINGS);

$opt_H && &die_usage;                                       #done
$opt_L && (@OCT_BASE = split(/\s*:\s*/,$opt_L));
$opt_l && (push(@OCT_BASE,split(/\s*:\s*/,$opt_l)));
$opt_X && (@OCT_BASE = @{&rm_dirs(\@OCT_BASE,$opt_X)});

if (not $opt_M)
{ $size="25,20";
} else {
    ($opt_M =~ m/^legal/i) and $size = '8.5,14';
    ($opt_M =~ m/^letter/i) and $size = '8.5,11';

    ($opt_M =~ m/^A0$/i) and $size = '33.1,46.8';
    ($opt_M =~ m/^A1$/i) and $size = '23.4,33.1';
    ($opt_M =~ m/^A2$/i) and $size = '16.5,23.4';
    ($opt_M =~ m/^A3$/i) and $size = '11.7,16.5';
    ($opt_M =~ m/^A4$/i) and $size = '8.3,11.7';
    ($opt_M =~ m/^A4dj$/i) and $size = '8.3,11.7';
    ($opt_M =~ m/^A5$/i) and $size = '5.8,8.3';
}

#if (not $opt_r) { $ratio = 'fill'; } else { $ratio = $opt_r; }
$ratio = $opt_r || 'fill';

if ($opt_d)
{
    $opt_l = $opt_p = 1;
}

#make sure it has a tailing slash.
if ($opt_B)
{
    ($opt_B !~ m{/$}) && ($opt_B .= q[/]);
}

########################################################################

$| = 1;
if (! @ARGV)
{
    &die_usage;
} else
{
    %mfhash  = &map_name_to_filename(@ARGV);
}

if ($opt_d)
{
    @myargv     = @ARGV;
    print join(' ',map { s/\.m/$opt_d/e;$_; } @ARGV),qq[ : ];
}

if ($opt_l) {
    %bdhash = &find_base_libs(@OCT_BASE);
    $alldepref  = &find_all_deps(\%mfhash,\%bdhash,0);
    print join(' ',@{$alldepref}),qq[\n];
} else {
    &print_head();
    %bdhash = &find_base_libs(@OCT_BASE);
    &find_all_deps(\%mfhash,\%bdhash,1);
    &print_tail();
}

$opt_X && (@OCT_BASE = @{&rm_dirs(\@OCT_BASE,$opt_X)});
########################################################################
sub
    rm_dirs
    #remove directories from OCT_BASE
{
    my $ob_ref = shift(@_);
    my $oX = shift(@_);
    my @excludeus = split(/\s*:\s*/,$oX);

    #FIX!


}

########################################################################
sub
    make_relative
    #just for the sake of opt_B#FOLDUP
{
    my $fullname = shift(@_);
    if ($opt_B)
    {
        $fullname =~ s{\Q$opt_B\E}{};
    }
    return $fullname;
}#UNFOLD
########################################################################
sub
    map_name_to_filename#FOLDUP
{
    my $mfile;
    my %mfiles;
    my $mfstub;
    while ($mfile = shift(@_))
    { 
        $mfstub = $mfile;
        $mfstub =~ s/^\s*(.*\/)?([^\/]+)\.m\s*$/$2/;
        $mfiles{$mfstub} = $mfile;
    }
    return %mfiles;
}#UNFOLD

########################################################################
sub
    find_base_libs#FOLDUP
{
    my $based;
    my %bdhash;
    my ($mfile,$mfstub);
    my @mfiles;
    while ($based = shift(@_))
    { 
#           print "|$based|\n";
        @mfiles = split(/\n/,qx(cd $based && find . -name '*.m'));
        while ($mfile = shift(@mfiles))
        {
            $mfstub = $mfile;
            $mfstub =~ s/.+\/([^\/]+)\.m/$1/;
            $mfile  =~ s/^\s*\.\//$based/;
            $bdhash{$mfstub} = $mfile;
            #print STDERR "|$mfstub| -> |$mfile| |$based|\n";
        }
    }
    return %bdhash;
}#UNFOLD

########################################################################
#returns array of all the dependencies as filename strings.
sub
    find_all_deps#FOLDUP
{
    my $mfhashref = shift(@_);
    my $bdhashref = shift(@_);
    my $doprint     = shift(@_);            #if 0, do not print anything out.
    my @mfhashlist = %{$mfhashref};
    my %bdhash = %{$bdhashref};
    my $output = [];
    my %globals;
    my $gname;
    my %doneok;
    my ($mfname,$mfloc);
    my ($aline,$acommand,$copyline);
    my %eegraph;                            #store as node::node in this hash set.
                              #prevents edges from being written multiple times?
    my %dangling = {};              #any command which has yet to be found.
                              #store vals a list of things which want to point in.
    my $pointsin;
    my $foundnewfunc;
    my $foundFuncPragma;            #for looking for %  #function fname stuff
    #my @myDependencies;       #every function that I call;

    my $edgestr = '';

    while ($mfname = shift(@mfhashlist))#FOLDUP
    {
        $mfloc  = shift(@mfhashlist);
        $mf_alias = ($opt_p)? &make_relative($mfloc) : $mfname;     #full names or not

        #prevent node -> self edges.
        $eegraph{qq(${mfname}::${mfname})} = 1;

        if ((! $opt_C) && $doprint)
        {
            print qq(subgraph cluster_$mfname {\n);
            print qq(rank=min\n);
            print qq(ordering=out\n);
        }
        #node
        $doprint && 
            print qq{$mfname [label="$mf_alias" shape=plaintext fontsize=44]\n};
        push (@{$output},$mf_alias);

        $doneok{$mfname} = 1;

        #open a file#FOLDUP
        open (FH,"$mfloc") || die "no open $mfloc, $!";

         while (! eof(FH))
         {
             $aline = ;
             chomp($aline);
             $foundFuncPragma       = 0;

             if ($aline =~ /^[^%]*end\s*%?\s*function/) { $mfname = ''; }

             if ($mfname)       #inside a function
             {
                 if ($opt_g || $opt_G)          #look for globals#FOLDUP
                 {
                        if ($aline =~ /global/)
                        {
                            $copyline       = $aline;
                            while ($copyline =~ s/(global\s+)([^;\s]+)(\s*;)/$1$3/)
                            {
                                $gname  = $2;
                                if (exists $globals{$gname})
                                {
                                    push(@{$globals{$gname}},$mfname);
                                } else {
                                    $globals{$gname}    = [$mfname];
                                }
                            }
                        }
                 }#UNFOLD

                    #look for #function pragma
                 $foundFuncPragma = ($aline =~ s/%\s*#function\s+(.+)$//);
                 if ($foundFuncPragma)
                 { 
                        $opt_D && (print STDERR "found a function pragma! |$1|\n");
                        #what a bummer that we can't just use this: the
                        #problem is that we don't really know when a function
                        #ends in .m code, b/c endfunction is not required. bummer.
                        #push (@myDependencies,split(/\s+/,$1));
                        #
                        #that is, what we would really like to do is just push onto a list
                        #every time we saw a command, then puke at the end of the function,
                        #but we do not know really when a function ends in matlab. oops.
                        foreach $acommand (split(/\s+/,$1))
                        {
                            $opt_D && (print STDERR "found a command! |$acommand|\n");
                            #push (@myDependencies,$acommand);

                            if (exists($bdhash{$acommand}))
                            {
                                $opt_D && (print STDERR "exists in bdhash (prolly means is a file to itself)\n");
                                if (! $eegraph{qq(${mfname}::${acommand})})
                                {
                                    if ($opt_C) { $doprint && print "$mfname -> $acommand\n";
                                    } else { $edgestr .= "$mfname -> $acommand\n"; }

                                    if (! $opt_m) { $eegraph{qq(${mfname}::${acommand})} = 1; }
                                } 

                                if (! $doneok{$acommand})
                                {
                                    $doneok{$acommand} = 1;
                                    push(@mfhashlist,$acommand,$bdhash{$acommand});
                                }
                            } else
                            {
                                if (exists($dangling{$acommand}))
                                { push(@{$dangling{$acommand}},$mfname);
                                } else { $dangling{$acommand} = [$mfname]; }
                            }
                        }
                 }

                 while ($aline =~ /([a-zA-Z0-9_]+)\s*\(/)#FOLDUP
                 {
                    $aline =~ s/([a-zA-Z0-9_]+)\s*\(//;
                    $acommand = $1;

                    $opt_D && (print STDERR "found a command! |$acommand|\n");
                    #push (@myDependencies,$acommand);

                    if (exists($bdhash{$acommand}))
                    {
                        $opt_D && (print STDERR "exists in bdhash (prolly means is a file to itself)\n");
                        if (! $eegraph{qq(${mfname}::${acommand})})
                        {
                            if ($opt_C) { $doprint && print "$mfname -> $acommand\n";
                            } else { $edgestr .= "$mfname -> $acommand\n"; }

                            if (! $opt_m) { $eegraph{qq(${mfname}::${acommand})} = 1; }
                        } 

                        if (! $doneok{$acommand})
                        {
                            $doneok{$acommand} = 1;
                            push(@mfhashlist,$acommand,$bdhash{$acommand});
                        }
                    } else
                    {
                        if (exists($dangling{$acommand}))
                        { push(@{$dangling{$acommand}},$mfname);
                        } else { $dangling{$acommand} = [$mfname]; }
                    }
                 }#UNFOLD
             } else             #not yet inside a function.
             {
                 $foundnewfunc = 0;
                 if ($aline =~ /^[^%]*function\s+[^=]*=\s*([a-zA-Z0-9_]+)\s*(\(|;|%|$)/)
                 {
                     $mfname = $1;$foundnewfunc = 1;
                 } elsif ($aline =~ /^[^%]*function\s+([a-zA-Z0-9_]+)\s*(\(|;|%|$)/)
                 {
                     $mfname = $1;$foundnewfunc = 1;
                 } 

                 if ($foundnewfunc)
                 {
                     #@myDependencies = ();
                    $opt_D && (print STDERR "now looking at function |$mfname|\n");
                     $eegraph{qq(${mfname}::${mfname})} = 1;
                     #subnode
                     $doprint && print "$mfname [shape=box]\n";

                     $doneok{$mfname} = 1;
                     $bdhash{$mfname} = 1;              #innocent enough since doneok is set too.

                     if (exists($dangling{$mfname}))
                     {
                         while ($pointsin = shift(@{$dangling{$mfname}}))
                         {
                                $doprint && print "$pointsin -> $mfname\n";
                         }
                     }
                 }
             }
         }
        close FH;#UNFOLD
        if (! $opt_C)
        {
            $doprint && print qq(}\n);
            $doprint && print $edgestr;
            $edgestr = '';
        }
    }#UNFOLD

    if ($doprint)
    {
        if ($opt_g)
        {
            foreach $key (keys(%globals))
            {
                print qq{$key [style=dotted label="$key" color=red shape=plaintext fontsize=44]\n};
                foreach $f (@{$globals{$key}})
                {
                    print qq{$f -> $key [color=red]\n};
                }
            }
        } elsif ($opt_G)
        {
            foreach $key (keys(%globals))
            {
                while (defined($g = shift(@{$globals{$key}})))
                {
#                   foreach $f (@{$globals{$key}}) { print qq{$g -- $f [color=red]\n}; }
                    foreach $f (@{$globals{$key}}) { print qq{$g -> $f [style=dotted label="$key" fontsize=30 fontcolor=red color=red]\n}; }
                }
            }
        }
    }

    return $output;
}#UNFOLD

########################################################################

sub
    print_head#FOLDUP
{
    if (! $opt_m)
    {
        print qq[strict ];
    }
#   if ($opt_G) { print qq[octavedep {\n]; } else { print qq[digraph octavedep {\n]; }
    print qq[digraph octavedep {\n];
    print qq[nslimit=15.0\n];
    print qq[mclimit=1.0\n];
    print qq[ratio="$ratio"\n];
    print qq[size="$size"\n];
}#UNFOLD

sub
    print_tail#FOLDUP
{
    print "}\n";
}#UNFOLD

########################################################################
sub 
    die_usage#FOLDUP
{
#   print STDERR "usage: perl $0 [-$OPTS] [-$VALOPTS val] octfiles\n\n";
    print STDERR "usage: perl $0 [-$OPTS] octfiles\n\n";

    if ($opt_H) 
    {
        %OPT_MEANINGS = 
            map {($a=$_)=~s/(.)+?[=:!]?[ifs]?/$1/;$a=>$OPT_MEANINGS{$_};}    
            keys %OPT_MEANINGS;
        @OPTS = split(//,$OPTS);
        while ($OP = shift(@OPTS)) {
            print STDERR "      $OP   $OPT_MEANINGS{$OP}\n";
        }
        print STDERR "\n";
    } 


    exit;
}#UNFOLD
########################################################################
__END__

为我工作...

于 2009-08-28T19:48:07.230 回答
0

另一种方法是排除不需要的文件夹: localdep = depfunresult(cellfun(@isempty,regexp(a,'toolbox'))); 您可以在那里使用任何正则表达式模式。

于 2009-02-26T22:58:52.690 回答
0

尽管 depfun 没有提供“ignore-builtins”选项,但它确实为我们提供了一个“-toponly”选项,我们可以在我们自己的递归函数中使用它,它确实排除了内置函数并运行得更快。以下是我的解决方案:

function new_file_list = fastdepfun(paths)
% new_file_list = fastdepfun(paths)
% paths = same input as you use with depfun

[file_list] = depfun(paths,'-toponly','-quiet');

% Remove builtins (implement this part however you like)
mroot = matlabroot;
file_list = file_list(~strncmp(file_list,mroot,length(mroot)));

% Remove files already inspected (otherwise we get stuck in an infinite loop)
new_file_list = setdiff(file_list,paths);

if ~isempty(new_file_list)
    new_file_list = fastdepfun(new_file_list);
end
new_file_list = unique([file_list; new_file_list]);
于 2012-01-26T20:39:36.000 回答