MATLAB: определить зависимости из 'командной строки', исключая встроенные зависимости

Есть ли способ определить все зависимости файла .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'

и т.п.

Список немного длиннее.

Дело в том, что я надеялся, что будет какая-то похожая функция или флаг, который удалит эти нежелательные зависимости.

Ответов (7)

Другой способ - просто исключить ненужные папки: localdep = depfunresult (cellfun (@ isempty, regexp (a, 'toolbox'))); Вы можете использовать здесь любой шаблон регулярного выражения.

Сегодня я наконец-то запустил этот скрипт, он основан на Windows Matlab, поскольку он выполняет вызов '! Findstr "something" file.txt'. (Я бы предпочел grep, но не знал эквивалента в Matlab.

Я собираюсь спросить своего босса, разрешено ли мне опубликовать его в обмене файлами Matlab, чтобы поделиться с другими, поэтому, надеюсь, я скоро обновлю это со ссылкой.

gnovice: У меня недостаточно представителей, чтобы прокомментировать комментарий gnovice к моему описанию, который я написал до написания кода.

Но в основном, чтобы определить, что он делает, берет имя файла всех файлов (разбитое на категорию типа файла), удаляет полный путь и расширение, использует вышеупомянутую команду! Findstr для поиска его в файле .m, который вы создаете. зависимость для и выводит это в файл temp.txt (это потому, что я не мог понять способ получить 1 или 0 или пустое возвращение на выходе команды)

вот разбивка того, что я лично ищу, чтобы определить, используется ли каждый файл:

.m: 'filename' или 'filename ('% покрывает регистр 'filename (' .mex *: то же, что и выше. 'работаю над этим, вероятно, завтра .txt: просто ищет' filename.txt '

С помощью этого метода у вас может получиться несколько дополнительных текстовых файлов или файлов .m, но главное здесь в том, что у вас должны быть по крайней мере все необходимые файлы.

Он также рекурсивно вызывает себя для всех зависимых файлов, чтобы их зависимости также учитывались.

-TaRDy

Хотя 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]);

Я давно написал код, чтобы сделать это для октавы. Я использую его в основном для создания файлов .dot для graphviz для визуализации зависимостей, но я также использую его в make-файлах для упаковки зависимостей при компиляции кода. К сожалению, это Perl-код, но вы можете запустить его из сценария, вызвав его через оболочку. он полностью рекурсивен.

чтобы запустить его, вам нужно изменить OCT_BASE, чтобы он указывал на корневой каталог вашего кода. (извините, это не известно о переменной пути Matlab). тогда я бы, вероятно, запустил его как perl octavedepgrapher.pl -l


#! /bin/sh
exec perl -x -S $0 ${1+"[email protected]"} # -*-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__

работает для меня...

Попробуйте DepSubFun из TMW FileExchange.

Вот пара ссылок, которые мне показались полезными, когда я написал простую функцию для создания оглавления для m-файла :

  • Тема, обсуждающая недокументированную функцию MLINTMEX
  • FDEP от Урса Шварца на MathWorks File Exchange
  • FARG от Урса Шварца на сайте обмена файлами 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).

Надеюсь, они вам пригодятся! знак равно

Спасибо за ответы.

Я не думаю, что это именно то, чего я пытаюсь достичь.

Я надеялся, что уже есть что-то, что определит локальные функции, вызываемые в основном m-файле, добавит их в список и продолжит просмотр каждой из них, пока не останется ни одной. Не похоже, что какое-либо из этих решений делает это

Я придумал схему, которую постараюсь реализовать. Это может быть немного грубой силы, и дизайн может измениться по мере того, как я над ним работаю, но вот концепция.

В этом первоначальном дизайне сделано довольно много предположений, но, поскольку это в основном для меня и некоторых других, я не думаю, что это будет большой проблемой для моего общего решения.

Типы файлов для поиска: .m .mat .mex * .txt (будут обновляться по мере необходимости)

Определите пути matlabpath и отсеивайте пути к панели инструментов (здесь предполагается, что ваши рабочие каталоги не называются набором инструментов или что у вас нет каких-либо специальных m-файлов, которые вы добавили в другие наборы инструментов)

Надеюсь, оставим вам только каталоги, которые вы используете и из которых можете вызывать функции. (также предполагается, что вы не запрограммировали жестко какой-либо тип [запустите 'C: \ random \ myscript.m']

часть грубой силы: найдите типы файлов, которые вас интересуют, и составьте список из них в вашем рабочем каталоге (pwd) и оставшиеся пути Matlab

удалите имена файлов, соответствующие имени файла в рабочем каталоге.

выполнить итерацию по поиску основного m-файла для каждого имени файла, если он найден, добавить его в массив зависимых файлов. удалить зависимые файлы из исходного списка. искать список зависимых файлов с "новым" исходным списком, повторять до тех пор, пока не останется ни одного файла или совпадений не будет вовсе.

Пока это только моя концепция, я также буду искать еще немного.