MATLAB: определить зависимости из «командной строки», исключая встроенные зависимости - PullRequest
3 голосов
/ 27 февраля 2009

Есть ли способ определения всех зависимостей файла .m и любых зависимостей файлов, которые он вызывает, с помощью команды в сценарии (командной строке)?

Ранее был такой вопрос, и он был действительно хорош, потому что предлагал использовать функцию depfun. НО проблема заключалась в том, что он выводит файлы, связанные с MATLAB , от которых он также зависит.

Пример: testing.m

disp('TESTING!!');

Вывод depfun («тестирование»)

'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 ]

2 голосов
/ 27 февраля 2009

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

  • Поток, обсуждающий недокументированную функцию MLINTMEX
  • FDEP Урса Шварца на MathWorks File Exchange
  • FARG Урса Шварца на MathWorks File Exchange

РЕДАКТИРОВАТЬ: Поскольку эта проблема пробудила мое любопытство, я начал пробовать несколько способов, которыми я мог бы подойти к нему. Найти зависимости от файлов .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).

Надеюсь, вы найдете это полезным! =) * 1 047 *

1 голос
/ 27 февраля 2009

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

0 голосов
/ 27 января 2012

Хотя 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]);
0 голосов
/ 28 августа 2009

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

у меня работает ...

0 голосов
/ 04 марта 2009

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

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

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

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

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

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

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

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

-TaRDy

0 голосов
/ 27 февраля 2009

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

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

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

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

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

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

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

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

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

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

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

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

0 голосов
/ 27 февраля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...