3 # ************************************************************
4 # Description : Process mpc command line options
5 # Author : Chad Elliott
6 # Create Date : 3/20/2003
7 # ************************************************************
9 # ************************************************************
11 # ************************************************************
19 # ************************************************************
21 # ************************************************************
30 print STDERR
"ERROR: $msg\n" if (defined $msg);
32 my $spaces = (' ' x
(length($base) + 8));
33 print STDERR
"$base v$version\n" .
34 "Usage: $base [-global <file>] [-include <directory>] [-recurse]\n" .
35 $spaces . "[-ti <dll | lib | dll_exe | lib_exe>:<file>] [-hierarchy]\n" .
36 $spaces . "[-template <file>] [-relative NAME=VAL] [-base <project>]\n" .
37 $spaces . "[-noreldefs] [-notoplevel] [-static] [-genins] [-use_env]\n" .
38 $spaces . "[-value_template <NAME+=VAL | NAME=VAL | NAME-=VAL>]\n" .
39 $spaces . "[-value_project <NAME+=VAL | NAME=VAL | NAME-=VAL>]\n" .
40 $spaces . "[-make_coexistence] [-feature_file <file name>] [-gendot]\n" .
41 $spaces . "[-expand_vars] [-features <feature definitions>]\n" .
42 $spaces . "[-exclude <directories>] [-name_modifier <pattern>]\n" .
43 $spaces . "[-apply_project] [-version] [-into <directory>]\n" .
44 $spaces . "[-gfeature_file <file name>] [-nocomments]\n" .
45 $spaces . "[-relative_file <file name>] [-for_eclipse]\n" .
46 $spaces . "[-workers <#>] [-workers_dir <dir> | -workers_port <#>]\n" .
47 $spaces . "[-language <";
49 my $olen = length($spaces) + 12;
52 my @keys = sort(Creator
::validLanguages
());
53 for(my $i = 0; $i <= $#keys; $i++) {
54 my $klen = length($keys[$i]);
57 print STDERR
"\n$spaces ";
60 print STDERR
$keys[$i];
69 $olen = length($spaces) + 8;
72 ## Sort the project types, but keep those that are the same with different
73 ## version numbers in the right order (i.e., vc8, vc9, vc10, vc11). The vc71
74 ## type is a special case and needs to stay between vc7 and vc8.
75 @keys = sort { if ($a ne 'vc71' && $b ne 'vc71' && $a =~ /^([^\d]+)(\d+)$/) {
76 my($a1, $a2) = ($1, $2);
77 if ($b =~ /^([^\d]+)(\d+)$/ && $a1 eq $1) {
83 for(my $i = 0; $i <= $#keys; $i++) {
84 my $klen = length($keys[$i]);
87 print STDERR
"\n$spaces ";
93 print STDERR
$keys[$i];
100 $spaces . "[files]\n\n";
103 " -base Add <project> as a base project to each generated\n",
104 " project file. Do not provide a file extension, the\n",
105 " .mpb extension will be tried first; if that fails the\n",
106 " .mpc extension will be tried.\n",
107 " -exclude Use this option to exclude directories or files when\n",
108 " searching for input files.\n",
109 " -expand_vars Perform direct expansion, instead of performing relative\n",
110 " replacement with either -use_env or -relative options.\n",
111 " -feature_file Specifies the feature file to read before processing.\n",
112 " The default feature file is default.features under the\n",
113 " config directory.\n",
114 " -features Specifies the feature list to set before processing.\n",
115 " -for_eclipse Generate files for use with eclipse. This is only\n",
116 " useful for make based project types.\n",
117 " -gendot Generate .dot files for use with Graphviz.\n",
118 " -genins Generate .ins files for use with prj_install.pl.\n",
119 " -gfeature_file Specifies the global feature file. The\n",
120 " default value is global.features under the\n",
121 " config directory.\n",
122 " -global Specifies the global input file. Values stored\n",
123 " within this file are applied to all projects.\n",
124 " -hierarchy Generate a workspace in a hierarchical fashion.\n",
125 " -include Specifies a directory to search when looking for base\n",
126 " projects, template input files and templates. This\n",
127 " option can be used multiple times to add directories.\n",
128 " -into Place all output files in a mirrored directory\n",
129 " structure starting at <directory>. This should be a\n",
130 " full path. If any project within the workspace is\n",
131 " referenced via a full path, use of this option is\n",
132 " likely to cause problems.\n",
133 " -language Specify the language preference; possible values are\n",
134 " [", join(', ', sort(Creator
::validLanguages
())), "]. The default is\n",
135 " ", Creator
::defaultLanguage
(), ".\n",
136 " -make_coexistence If multiple 'make' based project types are\n",
137 " generated, they will be named such that they can coexist.\n",
138 " -name_modifier Modify output names. The pattern passed to this\n",
139 " parameter will have the '*' portion replaced with the\n",
140 " actual output name. Ex. *_Static\n",
141 " -apply_project When used in conjunction with -name_modifier, it applies\n",
142 " the name modifier to the project name also.\n",
143 " -nocomments Do not place comments in the generated files.\n",
144 " -noreldefs Do not try to generate default relative definitions.\n",
145 " -notoplevel Do not generate the top level target file. Files\n",
146 " are still processed, but no top level file is created.\n",
147 " -recurse Recurse from the current directory and generate from\n",
148 " all found input files.\n",
149 " -relative Any \$() variable in an mpc file that is matched to NAME\n",
150 " is replaced by VAL only if VAL can be made into a\n",
151 " relative path based on the current working directory.\n",
152 " This option can be used multiple times to add multiple\n",
154 " -relative_file Specifies the relative file to read before processing.\n",
155 " The default relative file is default.rel under the\n",
156 " config directory.\n",
157 " -static Specifies that only static projects will be generated.\n",
158 " By default, only dynamic projects are generated.\n",
159 " -template Specifies the template name (with no extension).\n",
160 " -workers Specifies number of child processes to use to generate\n",
162 " -workers_dir The directory for storing temporary output files\n",
163 " from the child processes. The default is '/tmp/mpc'\n",
164 " If neither -workers_dir nor -workers_port is used,\n",
165 " -workers_dir is assumed.\n",
166 " -workers_port The port number for the parent listener. If neither\n",
167 " -workers_dir nor -workers_port is used, -workers_dir\n",
169 " -ti Specifies the template input file (with no extension)\n",
170 " for the specific type (ex. -ti dll_exe:vc8exe).\n",
171 " -type Specifies the type of project file to generate. This\n",
172 " option can be used multiple times to generate multiple\n",
173 " types. There is no longer a default.\n",
174 " -use_env Use environment variables for all uses of \$() instead\n",
175 " of the relative replacement values.\n",
176 " -value_project This option allows modification of a project variable\n",
177 " assignment. Use += to add VAL to the NAME's value.\n",
178 " Use -= to subtract and = to override the value.\n",
179 " This can be used to introduce new name value pairs to\n",
180 " a project. However, it must be a valid project\n",
182 " -value_template This option allows modification of a template input\n",
183 " name value pair. Use += to add VAL to the NAME's\n",
184 " value. Use -= to subtract and = to override the value.\n",
185 " -version Print the MPC version and exit.\n";
196 ## Clean up the path as much as possible. For some reason,
197 ## File::Spec->canonpath() on Windows doesn't remove trailing
198 ## /. from the path. (Current versions have fixed this, but
199 ## we'll leave the work-around in case users have an old Perl.)
200 my $p = File
::Spec
->canonpath($_[0]);
207 sub completion_command
{
208 my($self, $name, $types) = @_;
209 my $str = "complete $name " .
210 "'c/-/(gendot genins global include type template relative " .
211 "ti static noreldefs notoplevel feature_file use_env " .
212 "value_template value_project make_coexistence language " .
213 "hierarchy exclude name_modifier apply_project version " .
214 "expand_vars gfeature_file nocomments for_eclipse relative_file)/' " .
215 "'c/dll:/f/' 'c/dll_exe:/f/' 'c/lib_exe:/f/' 'c/lib:/f/' " .
216 "'n/-ti/(dll lib dll_exe lib_exe)/:' ";
218 $str .= "'n/-language/(";
219 my @keys = sort(Creator
::validLanguages
());
220 for(my $i = 0; $i <= $#keys; $i++) {
222 $str .= " " if ($i != $#keys);
224 $str .= ")/' 'n/-type/(";
226 @keys = sort keys %$types;
227 for(my $i = 0; $i <= $#keys; $i++) {
229 $str .= " " if ($i != $#keys);
240 my $defaults = shift;
260 my $language = ($defaults ? Creator
::defaultLanguage
() : undef);
261 my $dynamic = ($defaults ?
1 : undef);
262 my $comments = ($defaults ?
1 : undef);
263 my $reldefs = ($defaults ?
1 : undef);
264 my $toplevel = ($defaults ?
1 : undef);
265 my $use_env = ($defaults ?
0 : undef);
266 my $expandvars = ($defaults ?
0 : undef);
267 my $static = ($defaults ?
0 : undef);
268 my $recurse = ($defaults ?
0 : undef);
269 my $makeco = ($defaults ?
0 : undef);
270 my $applypj = ($defaults ?
0 : undef);
271 my $genins = ($defaults ?
0 : undef);
272 my $gendot = ($defaults ?
0 : undef);
273 my $foreclipse = ($defaults ?
0 : undef);
274 my $workers = ($defaults ?
0 : undef);
278 ## Process the command line arguments
279 for(my $i = 0; $i <= $#args; $i++) {
283 if ($arg eq '-apply_project') {
286 elsif ($arg eq '-complete') {
287 print $self->completion_command($name, $types) . "\n";
290 elsif ($arg eq '-base') {
292 if (!defined $args[$i]) {
293 $self->optionError('-base requires an argument');
296 push(@baseprojs, $args[$i]);
299 elsif ($arg eq '-type') {
301 if (!defined $args[$i]) {
302 $self->optionError('-type requires an argument');
305 my $type = lc($args[$i]);
306 if (defined $types->{$type}) {
307 my $call = $types->{$type};
309 foreach my $creator (@creators) {
310 if ($creator eq $call) {
315 push(@creators, $call) if (!$found);
318 $self->optionError("Invalid type: $args[$i]");
322 elsif ($arg eq '-exclude') {
324 if (defined $args[$i]) {
325 foreach my $exclude (split(',', $args[$i])) {
326 push(@exclude, DirectoryManager
::mpc_glob
(undef, $exclude));
330 $self->optionError('-exclude requires a ' .
331 'comma separated list argument');
334 elsif ($arg eq '-expand_vars') {
337 elsif ($arg eq '-feature_file') {
339 $feature_f = $args[$i];
340 if (!defined $feature_f) {
341 $self->optionError('-feature_file requires a file name argument');
344 elsif ($arg eq '-features') {
346 if (defined $args[$i]) {
347 push(@features, split(',', $args[$i]));
350 $self->optionError('-features requires a comma separated list argument');
353 elsif ($arg eq '-for_eclipse') {
356 elsif ($arg eq '-gfeature_file') {
358 $gfeature_f = $args[$i];
359 if (!defined $gfeature_f) {
360 $self->optionError('-gfeature_file ' .
361 'requires a file name argument');
364 elsif ($arg eq '-relative_file') {
366 $relative_f = $args[$i];
367 if (!defined $relative_f) {
368 $self->optionError('-relative_file ' .
369 'requires a file name argument');
372 elsif ($arg eq '-gendot') {
375 elsif ($arg eq '-genins') {
378 elsif ($arg eq '-global') {
381 if (!defined $global) {
382 $self->optionError('-global requires a file name argument');
385 elsif ($arg eq '-help') {
386 $self->optionError();
388 elsif ($arg eq '-hierarchy') {
391 elsif ($arg eq '-include') {
393 my $include = $args[$i];
394 if (!defined $include) {
395 $self->optionError('-include requires a directory argument');
398 ## If the specified include path is relative, expand it based on
399 ## the current working directory.
400 if ($include !~ /^[\/\\]/ &&
401 $include !~ /^[A-Za-z]:[\/\\]?
/) {
402 $include = DirectoryManager
::getcwd
() . '/' . $include;
405 push(@include, $include);
408 elsif ($arg eq '-into') {
411 if (!defined $into) {
412 $self->optionError('-into requires a directory argument');
415 elsif ($arg eq '-language') {
417 $language = $args[$i];
418 if (!defined $language) {
419 $self->optionError('-language requires a language argument');
421 elsif (!Creator
::isValidLanguage
($language)) {
422 $self->optionError("$language is not a valid language");
425 elsif ($arg eq '-make_coexistence') {
428 elsif ($arg eq '-name_modifier') {
430 my $nmod = $args[$i];
431 if (!defined $nmod) {
432 $self->optionError('-name_modifier requires a modifier argument');
438 elsif ($arg eq '-nocomments') {
441 elsif ($arg eq '-noreldefs') {
444 elsif ($arg eq '-notoplevel') {
447 elsif ($arg eq '-recurse') {
450 elsif ($arg eq '-template') {
452 $template = $args[$i];
453 if (!defined $template) {
454 $self->optionError('-template requires a file name argument');
457 elsif ($arg eq '-relative') {
461 $self->optionError('-relative requires a variable assignment argument');
464 if ($rel =~ /(\w+)\s*=\s*(.*)/) {
470 ## If the specified path is relative, expand it based on
471 ## the current working directory.
472 if ($val !~ /^[\/\\]/ &&
473 $val !~ /^[A-Za-z]:[\/\\]?
/) {
475 $val = DirectoryManager
::getcwd
() . '/' . $val;
477 ## Inform the user that we're changing the value that they
479 $self->warning("-relative value $orig has been changed to\n$val");
482 $relative{$name} = path_cleanup
($val);
485 $self->optionError('Invalid argument to -relative');
489 elsif ($arg eq '-workers') {
491 $workers = $args[$i];
493 if (!defined $workers) {
494 $self->optionError('-workers requires an argument');
497 elsif ($arg eq '-workers_dir') {
499 $workers_dir = $args[$i];
501 if (!defined $workers_dir) {
502 $self->optionError('-workers_dir requires an argument');
505 if (! -d
$workers_dir) {
506 $self->diagnostic("Creating temp directory $workers_dir");
507 unless (mkdir $workers_dir) {
508 $self->optionError("Unable to create temp directory $workers_dir");
512 elsif ($arg eq '-workers_port') {
514 $workers_port = $args[$i];
516 if (!defined $workers_port) {
517 $self->optionError('-workers_port requires an argument');
520 if ($workers_port < 0 || $workers_port > 65535) {
521 $self->optionError('valid -workers_port range is between 0 and 65535');
524 elsif ($arg eq '-ti') {
526 my $tmpi = $args[$i];
527 if (!defined $tmpi) {
528 $self->optionError('-ti requires a template input argument');
531 if ($tmpi =~ /((dll|lib|dll_exe|lib_exe):)?(.*)/) {
538 foreach my $type ('dll', 'lib', 'dll_exe', 'lib_exe') {
544 $self->optionError("Invalid -ti argument: $tmpi");
548 elsif ($arg eq '-use_env') {
551 elsif ($arg eq '-value_template') {
553 my $value = $args[$i];
554 if (!defined $value) {
555 $self->optionError('-value_template requires a variable assignment argument');
559 my $pc = new ProjectCreator
();
560 if ($pc->parse_assignment($value, \
@values)) {
561 $addtemp{$values[1]} = [] if (!defined $addtemp{$values[1]});
562 ## The extra parameter (3rd) indicates that this value was
563 ## specified on the command line. This "extra parameter" is
564 ## used in ProjectCreator::update_template_variable().
565 push(@
{$addtemp{$values[1]}}, [$values[0], $values[2], 1]);
567 my $keywords = ProjectCreator
::getKeywords
();
568 if (defined $$keywords{$values[1]}) {
569 $self->warning($values[1] . ' is a project keyword; you ' .
570 'should use -value_project instead.');
574 $self->optionError('Invalid argument to -value_template');
578 elsif ($arg eq '-value_project') {
580 my $value = $args[$i];
581 if (!defined $value) {
582 $self->optionError('-value_project requires a variable assignment argument');
586 my $pc = new ProjectCreator
();
587 if ($pc->parse_assignment($value, \
@values)) {
588 $addproj{$values[1]} = [] if (!defined $addproj{$values[1]});
589 push(@
{$addproj{$values[1]}}, [$values[0], $values[2]]);
592 $self->optionError('Invalid argument to -value_project');
596 elsif ($arg eq '-version') {
597 print 'MPC v', Version
::get
(), "\n";
600 elsif ($arg eq '-static') {
604 elsif ($arg =~ /^-/) {
605 $self->optionError("Unknown option: $arg");
608 push(@input, path_cleanup
($arg));
612 ## The following can be time consuming, so we'll only do it if we know
614 $self->dump_base_projects(\
@include) if ($self->get_debug_level());
616 return {'global' => $global,
617 'feature_file' => $feature_f,
618 'gfeature_file' => $gfeature_f,
619 'relative_file' => $relative_f,
620 'features' => \
@features,
621 'for_eclipse' => $foreclipse,
622 'include' => \
@include,
624 'comments' => $comments,
625 'creators' => \
@creators,
626 'baseprojs' => \
@baseprojs,
627 'template' => $template,
629 'dynamic' => $dynamic,
631 'relative' => \
%relative,
632 'reldefs' => $reldefs,
633 'workers' => $workers,
634 'workers_dir' => $workers_dir,
635 'workers_port' => $workers_port,
636 'toplevel' => $toplevel,
637 'recurse' => $recurse,
638 'addtemp' => \
%addtemp,
639 'addproj' => \
%addproj,
640 'make_coexistence' => $makeco,
641 'hierarchy' => $hierarchy,
642 'exclude' => \
@exclude,
643 'name_modifier' => $nmodifier,
644 'apply_project' => $applypj,
648 'language' => $language,
649 'use_env' => $use_env,
650 'expand_vars' => $expandvars,
656 my($self, $key, $options) = @_;
658 if (defined $options->{$key}) {
659 if (UNIVERSAL
::isa
($options->{$key}, 'ARRAY')) {
660 return 'ARRAY' if (defined $options->{$key}->[0]);
662 elsif (UNIVERSAL
::isa
($options->{$key}, 'HASH')) {
663 my @keys = keys %{$options->{$key}};
664 return 'HASH' if (defined $keys[0]);
674 sub dump_base_projects
{
675 my($self, $includes) = @_;
676 my $dp = new FileHandle
();
678 ## Go through each include directory and print all of the possible base
679 ## projects. Both .mpb and .mpc files can be base projects.
680 foreach my $dir (@
$includes) {
681 if (opendir($dp, $dir)) {
682 $self->debug("Base projects from $dir: " .
683 join(' ', grep(/\.mp[bc]$/, readdir($dp))));