#!/usr/bin/env perl
use
5.008;
our
$datadir
;
BEGIN {
$datadir
= $0;
unless
(
$datadir
=~ s@/[^/]+$@@ ) {
foreach
(
split
( /:/,
$ENV
{
'PATH'
} ),
'.'
) {
if
( -d
"$_/Mpp"
) {
$datadir
=
$_
;
last
;
}
}
}
$datadir
or
die
"makepp: can't find library files\n"
;
$datadir
=
eval
"use Cwd; cwd . '/$datadir'"
if
$datadir
=~ /^\./;
do
"$datadir/Mpp/DB.pm"
if
exists
$DB::
{sh};
if
( $^O =~ /^MSWin/ && $] < 5.008007 ) {
my
$file
=
"$datadir/Mpp/File.pm"
;
local
$_
=
"$file.broken"
;
unless
( -f ) {
rename
$file
,
$_
;
open
my
$in
,
'<'
,
$_
;
open
my
$out
,
'>'
,
$file
;
chmod
07777 & (
stat
)[2],
$file
;
while
( <
$in
> ) {
s/\blstat\b/
stat
/g;
s/-l _/0/g;
print
$out
$_
;
}
}
}
unshift
@INC
,
$datadir
;
}
our
$progname
;
$Mpp::Text::pod
=
'makepp_command'
;
BEGIN {
*MAKEPP
=
$Mpp::Text::N
[1] }
our
$error_found
;
our
$log_level
;
our
$original_cwd
=
$CWD_INFO
;
delete
$ENV
{ROOT};
our
$MAKEFLAGS
=
delete
$ENV
{MAKEFLAGS};
our
$MAKEPPFLAGS
=
delete
$ENV
{MAKEPPFLAGS};
our
%global_ENV
=
%ENV
;
%ENV
=
%global_ENV
;
our
(
$dont_build_dir_flag
,
$sandbox_warn_flag
,
$virtual_sandbox_flag
,
$warn_undef_var
);
our
$dry_run
;
our
$n_phony_messages
= 0;
our
$environment_override
= 0;
our
$implicitly_load_makefiles
= 1;
our
$final_rule_only
= 0;
our
$rm_stale_files
= 0;
our
@makepp_include_path
= file_info
$datadir
,
$original_cwd
;
our
$remake_makefiles
= 1;
our
$stop_on_race
= 0;
sub
build {
exists
$_
[0]{BUILD_HANDLE} and
return
$_
[0]{BUILD_HANDLE};
return
if
$error_found
;
my
$oinfo
=
$_
[0];
if
(
&Mpp::File::dont_read
) {
warn
"error: Not building inexistent `"
.
&absolute_filename
.
"' because it's marked for dont-read\n"
;
return
1;
}
if
(
&Mpp::File::dont_build
) {
if
(
exists
$_
[0]{xPHONY} ||
&file_exists
) {
Mpp::
log
BUILD_NOT
=>
$oinfo
if
$log_level
;
}
else
{
warn
"info: Not building inexistent `"
.
&absolute_filename
.
"' because it's marked for dont-build\n"
;
return
1
if
&Mpp::File::dont_build
== 2;
}
return
undef
$_
[0]{BUILD_HANDLE};
}
Mpp::
log
TRY
=>
$oinfo
if
$log_level
;
my
$rule
= Mpp::File::get_rule(
$oinfo
) ||
new Mpp::DefaultRule(
$oinfo
);
exists
$oinfo
->{BUILD_HANDLE} and
return
$oinfo
->{BUILD_HANDLE};
return
if
exists
$rule
->{xSCANNING};
if
(
$rm_stale_files
&&
&Mpp::File::is_stale
) {
warn
'error: Not building '
,
$oinfo
,
" because it's a stale generated file\n"
;
&Mpp::File::unlink
;
++
$Mpp::failed_count
;
return
1;
}
local
$implicitly_load_makefiles
if
$implicitly_load_makefiles
&&
$rule
->{MAKEFILE} &&
exists
$rule
->{MAKEFILE}{xRECURSIVE_MAKE};
undef
$oinfo
->{BUILD_HANDLE};
local
$rule
->{xSCANNING};
Mpp::
log
USE
=>
$rule
if
$log_level
;
my
$dont_scan
=
$final_rule_only
&& !UNIVERSAL::isa(
$rule
,
'Mpp::DefaultRule'
) &&
!
exists
$oinfo
->{xPHONY};
warn
'info: Ignoring dependencies of '
,
&absolute_filename
,
" because --final-rule-only is specified\n"
if
$dont_scan
;
my
(
$all_targets
,
$all_dependencies
,
$command_string
,
$env
) =
$rule
->find_all_targets_dependencies(
$oinfo
,
$dont_scan
);
Mpp::
log
DEPEND
=>
$all_targets
,
$all_dependencies
if
$log_level
;
my
@build_handles
;
if
(
exists
$rule
->{SCAN_FAILED} ) {
die
unless
$rule
->{SCAN_FAILED};
push
@build_handles
,
$rule
->{SCAN_FAILED};
my
$reason
=
''
;
if
( UNIVERSAL::isa
$rule
->{SCAN_FAILED},
'Mpp::File'
) {
my
$finfo
=
$rule
->{SCAN_FAILED};
my
(
$handle
,
$next
);
while
(
$handle
=
$finfo
->{BUILD_HANDLE} and
exists
$handle
->{STATUS} and
$next
=
$handle
->{STATUS} and
UNIVERSAL::isa
$next
,
'Mpp::File'
) {
$finfo
=
$next
;
}
$reason
=
', because '
. absolute_filename(
$finfo
) .
' failed to build'
;
}
warn
'error: Not building '
.
&absolute_filename
.
" because the dependencies could not be computed $reason\n"
;
}
my
$handle
;
if
(
@$all_dependencies
and
$Mpp::keep_going
|| !
exists
$rule
->{SCAN_FAILED} ) {
local
$Mpp::indent_level
=
$Mpp::indent_level
+ 2;
foreach
my
$dep
(
@$all_dependencies
) {
next
if
exists
$dep
->{BUILD_HANDLE} && !
defined
$dep
->{BUILD_HANDLE};
$handle
= when_done build(
$dep
),
$Mpp::Text::N
[0],
ERROR
=>
$rm_stale_files
?
sub
{
if
(
grep
!(
exists
$_
->{xPHONY} || file_exists
$_
),
@$all_dependencies
) {
for
my
$finfo
(
@$all_targets
) {
Mpp::
log
DEL_STALE
=>
$finfo
if
$log_level
;
Mpp::File::
unlink
$finfo
;
CORE::
unlink
Mpp::File::build_info_fname
$finfo
;
}
}
$dep
;
} :
$dep
;
$handle
and
push
@build_handles
,
$handle
;
}
}
$handle
= when_done
@build_handles
, \
&build_dependencies_done
,
[
$oinfo
,
$all_targets
,
$all_dependencies
,
$command_string
,
$rule
,
$env
,
$dont_scan
]
and
exists
$handle
->{STATUS} && !
$handle
->{STATUS} and
undef
$handle
;
foreach
my
$target
(
@$all_targets
) {
$target
->{BUILD_HANDLE} =
$handle
;
}
$Mpp::parallel_make
or
wait_for
$oinfo
->{BUILD_HANDLE};
$oinfo
->{BUILD_HANDLE};
}
sub
build_dependencies_done {
my
(
$oinfo
,
$all_targets
,
$all_dependencies
,
$command_string
,
$rule
,
$env
,
$dont_scan
) =
@_
;
return
if
$error_found
;
for
my
$dep
(
@$all_dependencies
) {
Mpp::File::check_for_change
$dep
;
warn
'Target '
,
&absolute_filename
,
' depends on temporary file '
,
absolute_filename(
$dep
),
"\n"
if
exists
$dep
->{xTEMP};
}
my
$is_recursive_make
=
defined
$Mpp::Recursive::command
&&
$command_string
=~ /\brecursive_makepp\b/;
my
$rebuild_needed
;
my
$dont_build
;
my
$out_of_sandbox
;
my
@targets_to_get_from_repository
;
my
@targets_to_get_from_build_cache
;
my
$build_cwd
=
$rule
->build_cwd;
my
$build_check_method
=
$rule
->build_check_method;
my
$sig_method
=
$rule
->signature_method;
if
(
$is_recursive_make
) {
$rebuild_needed
= 1;
Mpp::
log
BUILD_RECURSIVE
=>
$all_targets
if
$log_level
;
}
elsif
(
$dont_scan
) {
$rebuild_needed
= 1;
Mpp::
log
BUILD_FINAL
=>
$all_targets
if
$log_level
;
}
else
{
target_loop:
foreach
my
$target
(
@$all_targets
) {
last
if
$rebuild_needed
&&
$dont_build
;
if
( Mpp::File::dont_build
$target
) {
$dont_build
=
$target
;
next
target_loop;
}
unless
( Mpp::File::in_sandbox
$target
) {
$out_of_sandbox
=
$target
;
$dont_build
=
$target
unless
$sandbox_warn_flag
;
}
next
target_loop
if
$rebuild_needed
or
exists
$target
->{xTEMP};
if
(
exists
$target
->{xPHONY} ) {
$rebuild_needed
= 1;
Mpp::
log
BUILD_PHONY
=>
$target
if
$log_level
;
next
target_loop;
}
Mpp::File::
mkdir
$target
->{
'..'
}
if
exists
$target
->{
'..'
}{xIN_REPOSITORY} && !file_exists
$target
->{
'..'
};
if
(
$build_check_method
->build_check(
$target
,
$all_dependencies
,
$command_string
,
$build_cwd
,
$sig_method
,
$env
)) {
if
(
exists
$target
->{ALTERNATE_VERSIONS} ) {
my
$is_src_file
= UNIVERSAL::isa
$rule
,
'Mpp::DefaultRule'
;
for
( @{
$target
->{ALTERNATE_VERSIONS}} ) {
if
(
$rm_stale_files
&&
$is_src_file
&& Mpp::File::was_built_by_makepp
$_
) {
Mpp::
log
REP_SKIP
=>
$_
if
$log_level
;
next
;
}
Mpp::
log
REP_CHECK
=>
$_
if
$log_level
;
if
(
$is_src_file
||
!
$build_check_method
->build_check_from_build_info(
$_
,
$all_dependencies
,
$command_string
,
$build_cwd
,
$sig_method
,
$env
)) {
push
@targets_to_get_from_repository
,
$target
,
$_
;
next
target_loop;
}
}
}
if
(
defined
$Mpp::BuildCache::used
and
my
$build_cache
= !
$Mpp::BuildCache::write_only
&&
$rule
->build_cache ) {
my
$bc_key
=
$build_check_method
->
build_cache_key(
$target
,
$all_dependencies
,
$command_string
,
$build_cwd
,
$sig_method
,
$env
);
if
(
$bc_key
) {
my
$bc_entry
=
$build_cache
->lookup_file(
$bc_key
);
if
(
$bc_entry
) {
Mpp::
log
BC_FOUND
=>
$target
,
$bc_key
if
$log_level
;
push
@targets_to_get_from_build_cache
,
$bc_entry
,
$target
;
next
target_loop;
}
else
{
Mpp::
log
BC_NONE
=>
$target
,
$bc_key
if
$log_level
;
}
}
else
{
Mpp::
log
BC_NO_KEY
=>
$target
if
$log_level
;
}
}
$rebuild_needed
= 1;
next
target_loop;
}
}
}
$rebuild_needed
||= Mpp::BuildCache::get(
$rule
, \
@targets_to_get_from_build_cache
)
if
@targets_to_get_from_build_cache
;
if
(
$rebuild_needed
) {
if
(
$dont_build
) {
warn
'error: Not building '
. absolute_filename(
$oinfo
) .
' because '
. absolute_filename(
$dont_build
) .
" (a target of its rule) was marked for dont-build or out-of-sandbox\n"
;
return
1;
}
warn
'Building '
, absolute_filename(
$out_of_sandbox
),
", which is outside the sandbox\n"
if
$out_of_sandbox
;
if
(
$is_recursive_make
) {
foreach
my
$target
(
@$all_targets
) {
delete
$target
->{BUILD_HANDLE};
}
}
unless
(
$is_recursive_make
||
$dry_run
) {
foreach
my
$tinfo
(
@$all_targets
) {
++
$tinfo
->{BUILDING}
if
$virtual_sandbox_flag
;
if
(
grep
$_
, Mpp::File::build_info_string
$tinfo
,
qw'FROM_REPOSITORY LINKED_TO_CACHE'
or
!
exists
$tinfo
->{xPHONY} && Mpp::File::check_for_change(
$tinfo
),
exists
$tinfo
->{xEXISTS} &&
(
my
$nw
= !Mpp::File::is_writable_owner
$tinfo
) ) {
Mpp::
log
REMOVE
=>
$tinfo
,
$nw
?
'not writable'
:
'repository or build cache import'
if
$log_level
;
Mpp::File::
unlink
$tinfo
;
}
elsif
(
exists
$tinfo
->{BUILD_INFO}{SYMLINK} ) {
$tinfo
->{TEMP_BUILD_INFO} =
delete
$tinfo
->{BUILD_INFO};
next
;
}
elsif
(
exists
$tinfo
->{TEMP_BUILD_INFO} ) {
next
;
}
Mpp::File::clear_build_info
$tinfo
;
}
}
$Mpp::critical_sections
++;
my
$execute_handle
=
$dry_run
&& !
$is_recursive_make
?
$rule
->print_command(
$command_string
) :
$rule
->execute(
$command_string
,
$all_targets
,
$all_dependencies
);
shift
;
my
$handle
= when_done
$execute_handle
, \
&build_target_done
, \
@_
,
ERROR
=>
sub
{
$error_found
=
$_
[0]
if
$_
[0] =~ /^signal (?:
$Mpp::int_signo
|
$Mpp::quit_signo
)$/os;
$error_found
||=
$_
[0]
unless
$Mpp::keep_going
;
$Mpp::failed_count
+=
@$all_targets
;
warn
'error: Failed to build target'
, (
@$all_targets
>1 ?
's'
:
''
),
map
(
' `'
.absolute_filename(
$_
).
"'"
,
@$all_targets
),
" [$_[0]]\n"
;
Mpp::
log
RULE_FAILED
=>
$rule
if
$log_level
;
foreach
my
$tinfo
(
@$all_targets
) {
next
if
exists
$tinfo
->{xPHONY};
may_have_changed
$tinfo
;
my
$deref
= dereference
$tinfo
;
may_have_changed
$deref
if
$tinfo
!=
$deref
;
unless
(
$is_recursive_make
||
$dry_run
||
ref
(
$rule
) eq
'Mpp::DefaultRule'
) {
Mpp::File::set_build_info_string
$tinfo
,
BUILD_SIGNATURE
=>
'FAILED'
,
COMMAND
=>
"|FAILED|$command_string"
,
CWD
=> relative_filename
$rule
->{MAKEFILE}{CWD},
$tinfo
->{
'..'
};
}
}
&Mpp::File::update_build_infos
unless
$dry_run
;
$Mpp::critical_sections
--;
propagate_pending_signals;
return
$_
[0];
};
return
$handle
;
}
Mpp::Repository::get(
splice
@targets_to_get_from_repository
, 0, 2 )
and
return
1
while
@targets_to_get_from_repository
;
Mpp::
log
UP_TO_DATE
=>
$all_targets
if
$log_level
;
if
( UNIVERSAL::isa
$build_check_method
,
'Mpp::BuildCheck::exact_match'
) {
$rule
->cache_scaninfo([
grep
{
!
exists
$_
->{xPHONY} &&
(Mpp::File::build_info_string
$_
,
'CWD'
or
''
) eq relative_filename
$build_cwd
,
$_
->{
'..'
}
}
@$all_targets
]);
}
foreach
my
$tinfo
(
@$all_targets
) {
Mpp::File::set_rule
$tinfo
,
undef
;
}
undef
;
}
sub
build_target_done {
my
(
$all_targets
,
$all_dependencies
,
$command_string
,
$rule
,
$env
) =
@_
;
my
$implicit_phony
= 0;
eval
{
my
$sig_method
=
$rule
->signature_method;
my
$build_cwd
=
$rule
->build_cwd;
my
$is_recursive_make
=
defined
$Mpp::Recursive::command
&&
$command_string
=~ /\brecursive_makepp\b/;
my
$build_cache
=
defined
$Mpp::BuildCache::used
&&
$rule
->build_cache;
my
$build_check_method
=
$rule
->build_check_method;
Mpp::
log
SUCCESS
=>
$rule
,
$all_targets
if
$log_level
;
my
@built_targets
;
my
$include
=
$rule
->{INCLUDE};
my
%uniq
;
if
(
$include
) {
if
(
$rule
->{INCLUDE_MD5} ?
$rule
->{INCLUDE_MD5} ne Mpp::Signature::md5::signature(
$include
) : 1 ) {
$uniq
{
sprintf
'%x'
,
$_
} =
$_
for
@$all_dependencies
;
for
my
$tinfo
(
@$all_targets
) {
delete
$uniq
{
sprintf
'%x'
,
$_
}
for
$rule
->expand_additional_deps(
$tinfo
);
}
Mpp::
log
LOAD_INCL
=>
$include
,
$rule
if
$Mpp::log_level
;
local
$Mpp::Makefile::rule_include
= 2;
$rule
->{MAKEFILE}->read_makefile(
$include
);
}
else
{
undef
$include
;
}
}
foreach
my
$tinfo
(
@$all_targets
) {
next
if
exists
$tinfo
->{xPHONY};
if
(
$include
) {
$uniq
{
sprintf
'%x'
,
$_
} =
$_
for
$rule
->expand_additional_deps(
$tinfo
);
}
may_have_changed
$tinfo
;
my
$deref
= dereference
$tinfo
;
may_have_changed
$deref
if
$tinfo
!=
$deref
;
push
@built_targets
,
$tinfo
;
}
$all_dependencies
=
$rule
->sorted_dependencies(
values
%uniq
)
if
keys
%uniq
;
$rule
->cache_scaninfo(\
@built_targets
);
my
$sorted_dep_names
=
join
"\cA"
,
map
relative_filename(
$_
,
$build_cwd
),
@$all_dependencies
;
my
$sorted_dep_sigs
=
join
"\cA"
,
map
{
$sig_method
->signature(
$_
) ||
''
}
@$all_dependencies
;
foreach
my
$tinfo
(
@built_targets
) {
unless
(
$is_recursive_make
||
$dry_run
) {
if
(
my
$tsig
=
$sig_method
->signature(
$tinfo
)) {
my
@extra
;
if
(
%$env
) {
my
@deps
=
sort
keys
%$env
;
@extra
= (
ENV_DEPS
=>
join
(
"\cA"
,
@deps
),
ENV_VALS
=>
join
(
"\cA"
, @{
$env
}{
@deps
} )
);
}
if
(
$tinfo
->{LINK_DEREF} &&
$tinfo
->{LSTAT}[Mpp::File::STAT_NLINK] == 1 ) {
Mpp::
log
SYMLINK
=>
$tinfo
if
$log_level
;
my
$link
=
readlink
absolute_filename
$tinfo
;
my
$linkee
= file_info
$link
,
$tinfo
->{
'..'
};
$tinfo
->{LINK_DEREF} ||=
$linkee
;
if
(
grep
$_
==
$linkee
,
@$all_dependencies
,
@$all_targets
) {
push
@extra
,
'SYMLINK'
,
$link
;
}
else
{
warn
$rule
->source,
': `'
. absolute_filename_nolink(
$tinfo
) . "
' is a symbolic link to `$link'
,
which is neither a dependency of this rule nor a target.
That means makepp can't guarantee a correct build of that file.
Your rule should in essence follow one of these two schemes:
link
: linkee
&ln
--force --resolve \$(input) \$(output)
linkee
link
:
&echo
Somehow produce linkee -o \$(output)
&ln
-fr \$(outputs)\n";
}
}
Mpp::File::set_build_info_string
$tinfo
,
SORTED_DEPS
=>
$sorted_dep_names
,
DEP_SIGS
=>
$sorted_dep_sigs
,
BUILD_SIGNATURE
=>
$tsig
,
COMMAND
=>
$command_string
,
CWD
=> relative_filename(
$build_cwd
,
$tinfo
->{
'..'
} ),
ARCH
=> ARCHITECTURE,
@extra
;
if
(
$build_cache
&& !
$Mpp::BuildCache::read_only
and
my
$bc_key
=
$build_check_method
->
build_cache_key(
$tinfo
,
$all_dependencies
,
$command_string
,
$build_cwd
,
$sig_method
,
$env
)) {
unless
(
$build_cache
->cache_file(
$tinfo
,
$bc_key
, \
my
$reason
)) {
my
$msg
=
'Exporting of '
. absolute_filename(
$tinfo
) .
" to the build cache failed because $reason\n"
;
&$Mpp::BuildCache::error_hook
(
$msg
)
if
$Mpp::BuildCache::error_hook
;
if
( !
$stop_on_race
&&
$msg
=~ s/ \(OK\)$//m ) {
warn
"info: ${msg}This might be due to concurrent access, so we'll just skip exporting this target.\n"
;
}
else
{
die
$msg
;
}
}
}
}
elsif
( !
exists
$tinfo
->{xTEMP} ) {
my
$mfile
=
$rule
->{MAKEFILE} ||
$tinfo
->{
'..'
}{MAKEINFO};
if
(
$mfile
&&
$mfile
->expand_variable(
'makepp_require_phony'
,
$rule
->source )) {
$implicit_phony
++;
}
elsif
( UNIVERSAL::isa
$rule
,
'Mpp::DefaultRule'
) {
warn
'error: There is no rule to build inexistent '
. absolute_filename(
$tinfo
) .
"\n"
;
}
elsif
(
$n_phony_messages
++ ) {
warn
'Target '
. absolute_filename(
$tinfo
) .
" is probably also phony.\n"
;
}
else
{
warn
'I attempted to build '
. absolute_filename(
$tinfo
) . ',
but
after
successfully executing the commands, the target does not exist.
Perhaps it is a phony target? If so, the rule defining it should be modified
like this:
$(phony
', relative_filename($tinfo, $build_cwd), '
): dependencies
actions
or you could also declare it as phony like this:
.PHONY: ', relative_filename(
$tinfo
,
$build_cwd
),
"\n"
;
}
undef
$tinfo
->{xPHONY};
$tinfo
->{SIGNATURE} = 9e99;
}
}
undef
$tinfo
->{xASSUME_NEW}
if
$dry_run
;
undef
$tinfo
->{BUILD_HANDLE}
unless
$is_recursive_make
;
Mpp::File::set_rule(
$tinfo
,
undef
);
}
my
$real
=
ref
(
$rule
) ne
'Mpp::DefaultRule'
;
for
(
@$all_targets
) {
if
(
exists
$_
->{xPHONY} ) { ++
$Mpp::n_phony_targets_built
}
elsif
(
$real
) { ++
$Mpp::n_files_changed
}
}
};
my
$error
= $@;
&Mpp::File::update_build_infos
unless
$dry_run
;
if
(
$implicit_phony
) {
warn
$error
if
$error
;
return
-2;
}
$Mpp::critical_sections
--;
propagate_pending_signals;
die
$error
if
$error
;
undef
;
}
our
(
$stop
,
$loop
);
sub
maybe_stop {
$stop
= 1
if
$loop
and !
defined
$stop
;
if
(
$stop
) {
$stop
= 0;
print
"\nmakepp: Stopped before building anything. Kill it; or continue with"
,
-t() ?
" one of:\n\n\tfg\tbg"
:
":\n\n"
,
"\tkill -CONT $$\n\n"
;
&flush_log
;
kill
STOP
=> $$;
}
}
sub
makepprc {
return
if
grep
/^-(?:[h?V]|-help|-version)$/,
@ARGV
;
my
$finfo
= Mpp::Subs::f_find_first_upwards
'.makepprc'
, {
CWD
=>
$CWD_INFO
},
'startup'
, \1;
return
if
!
$finfo
or
exists
$finfo
->{xMAKEPPRC};
return
unless
$finfo
->{LSTAT}[Mpp::File::STAT_SIZE];
open
my
$fh
,
'<'
, absolute_filename
$finfo
or
die
"$0: cannot open `.makepprc'--$!\n"
;
local
$/;
unshift
@ARGV
,
$finfo
->{
'..'
} ==
$CWD_INFO
?
unquote_split_on_whitespace <
$fh
> :
(
'-C'
,
$finfo
->{
'..'
}, unquote_split_on_whitespace( <
$fh
> ),
'-C'
,
$CWD_INFO
);
close
$fh
;
undef
$finfo
->{xMAKEPPRC};
}
my
@do_build
;
our
@ignore_opts
;
if
(
$ENV
{MAKEPP_IGNORE_OPTS} ) {
my
(
@lx
,
@l
,
@sx
,
@s
);
for
my
$opt
(
split
' '
,
$ENV
{MAKEPP_IGNORE_OPTS} ) {
if
(
$opt
=~ /^--(.+)=/ ) {
push
@lx
, $1;
}
elsif
(
$opt
=~ /^--(.+)/ ) {
push
@l
, $1;
}
elsif
(
$opt
=~ /^-(.)./ ) {
push
@sx
, $1;
}
elsif
(
$opt
=~ /^-(.)/ ) {
push
@s
, $1;
}
else
{
die
"\$MAKEPP_IGNORE_OPTS: '$opt' not understood\n"
;
}
}
my
$nop
;
local
$" =
''
;
if
(
@lx
||
@sx
) {
my
$lx
=
@lx
?
join
'|'
,
@lx
:
'DUMMY'
;
$lx
=
qr/$lx/
;
my
$sx
=
@sx
> 1 ?
qr/[@sx]/
:
$sx
[0];
push
@ignore_opts
, [
$sx
,
$lx
, \
$nop
, 1];
}
if
(
@l
||
@s
) {
my
$l
=
@l
?
join
'|'
,
@l
:
'DUMMY'
;
$l
=
qr/$l/
;
my
$s
=
@s
> 1 ?
qr/[@s]/
:
$s
[0];
push
@ignore_opts
, [
$s
,
$l
, \
$nop
];
}
}
sub
parse_command_line(\%@) {
local
$_
;
my
$this_ENV
=
shift
;
my
@targets
;
my
@initial_makefiles
;
my
@makefiles
;
my
@include_path
=
@makepp_include_path
;
my
%command_line_vars
;
my
$root_makefile
;
my
$target_cwd
=
$CWD_INFO
;
my
$tmp
;
&makepprc
;
while
(
@ARGV
) {
Mpp::Text::getopts \
%command_line_vars
, 1,
@ignore_opts
,
@_
,
[
'c'
,
qr/root(?:[-_]?dir(?:ectory)?)?/
, \
$tmp
,
undef
,
sub
{
$tmp
= Mpp::Makefile::find_root_makefile_upwards
$CWD_INFO
or
$CWD_INFO
->{ROOT}
or
die
"$0: No RootMakeppfile(.mk) found above `"
, absolute_filename(
$CWD_INFO
),
"'\n"
;
if
(
$tmp
) {
$root_makefile
||=
$tmp
;
$tmp
=
$tmp
->{
'..'
};
}
else
{
$tmp
=
$CWD_INFO
->{ROOT};
}
chdir
$tmp
;
$target_cwd
=
$tmp
;
&makepprc
;
}],
[
qw(C directory)
, \
$tmp
, 1,
sub
{
if
(
ref
$tmp
) {
chdir
$tmp
;
return
;
}
$tmp
= file_info
$tmp
;
Mpp::File::
mkdir
$tmp
;
chdir
$tmp
;
$root_makefile
||= Mpp::Makefile::find_root_makefile_upwards
$tmp
;
$target_cwd
=
$tmp
;
&makepprc
;
}],
[
undef
,
qr/dump[-_]?makep*file/
, \
$tmp
, 1,
sub
{
open
my
$fh
,
'>'
,
$tmp
or
die
"Failed to write $tmp--$!"
;
push
@Mpp::close_fhs
,
$fh
;
$CWD_INFO
->{DUMP_MAKEFILE} =
$fh
;
}],
[
'e'
,
qr/env(?:ironment)?[-_]?overrides?/
, \
$environment_override
],
[
'f'
,
qr/(?:make)?file/
, \
$tmp
, 1,
sub
{
if
(
$tmp
eq
'-'
) {
$tmp
= Mpp::Subs::f_mktemp
''
;
open
my
$fh
,
'>'
,
$tmp
;
local
$/;
syswrite
$fh
, <>;
close
$fh
;
}
push
@makefiles
, [file_info(
$tmp
),
$CWD_INFO
];
}],
[
qw(F makeppfile)
, \
$tmp
, 1,
sub
{
$tmp
= file_info
$tmp
;
my
$mdir
=
$tmp
;
Mpp::File::is_or_will_be_dir
$tmp
or
$mdir
=
$tmp
->{
'..'
};
push
@makefiles
, [
$tmp
,
$mdir
];
Mpp::File::
mkdir
(
$mdir
);
$root_makefile
||= Mpp::Makefile::find_root_makefile_upwards
$mdir
;
$target_cwd
=
$mdir
;
}],
[
'I'
,
qr/include(?:[-_]?dir)?/
, \
$tmp
, 1,
sub
{
push
@include_path
, file_info
$tmp
;
}],
[
undef
,
qr/load[-_]?make(?:pp)?file/
, \
$tmp
, 1,
sub
{
$tmp
= file_info
$tmp
;
my
$mdir
=
$tmp
;
Mpp::File::is_or_will_be_dir(
$tmp
) or
$mdir
=
$tmp
->{
'..'
};
push
@initial_makefiles
, [
$tmp
,
$mdir
];
Mpp::File::
mkdir
(
$mdir
);
}],
[
'o'
,
qr/(?:assume[-_]?old|old[-_]?file)/
, \
$tmp
, 1,
sub
{
$tmp
= file_info
$tmp
;
$tmp
->{ASSUME_UNCHANGED} = 1;
our
$assume_unchanged_dir_flag
= 1
if
is_or_will_be_dir
$tmp
;
}],
[
'r'
,
qr/no[-_]?builtin[-_]?rules/
, \
$command_line_vars
{makepp_no_builtin}],
[
qw(R repository)
, \
$tmp
, 1,
sub
{ Mpp::Subs::s_repository
$tmp
}],
[
'W'
,
qr/(?:what[-_]?if|assume[-_]?new|new[-_]?file)/
, \
$tmp
, 1,
sub
{
undef
file_info(
$tmp
)->{xASSUME_NEW};
}],
@Mpp::common_opts
;
delete
$command_line_vars
{makepp_no_builtin}
unless
defined
$command_line_vars
{makepp_no_builtin};
push
@targets
, file_info
shift
@ARGV
,
$target_cwd
if
@ARGV
;
}
close
DATA;
@_
= ();
unless
(
defined
$MAKEFLAGS
) {
my
$flags
=
(
$environment_override
?
'e'
:
''
) .
(
$Mpp::keep_going
?
'k'
:
''
) .
(
$dry_run
?
'n'
:
''
) .
(
$Mpp::quiet_flag
?
's'
:
''
) .
(
$Mpp::log_level
== 1 ?
'v'
:
''
);
$MAKEFLAGS
=
join
' '
,
(
$flags
?
$flags
: ()),
(
$Mpp::implicitly_load_makefiles
? () :
'--noimplicitload'
),
(
$Mpp::log_level
? () :
'--nolog'
),
(
$Mpp::print_directory
? () :
'--noprintdirectory'
),
(
$warn_undef_var
?
'--warnundef'
: ());
}
$root_makefile
||= Mpp::Makefile::find_root_makefile_upwards(
$target_cwd
);
if
(
$root_makefile
) {
unshift
@initial_makefiles
, [
$root_makefile
,
$root_makefile
->{
'..'
}];
my
$build_root
=
$root_makefile
->{
'..'
};
for
(
@do_build
) {
my
$saw_build_root
;
$_
=
$_
->{
'..'
}
unless
$_
==
$Mpp::File::root
;
while
( !
exists
$_
->{DONT_BUILD} ) {
$saw_build_root
++
if
$_
==
$build_root
;
if
(
$_
==
$Mpp::File::root
) {
if
(
$saw_build_root
) {
$build_root
->{DONT_BUILD} = 1;
warn
"info: --do-build not overriding --dont-build implies not building anything else.\n"
;
@do_build
= ();
}
last
;
}
$_
=
$_
->{
'..'
};
}
}
$dont_build_dir_flag
= 1;
exists
$build_root
->{DONT_BUILD} or
undef
$build_root
->{DONT_BUILD};
exists
$Mpp::File::root
->{DONT_BUILD} or
$Mpp::File::root
->{DONT_BUILD} = 2;
}
my
$makecmdgoals
=
join
(
' '
,
map
relative_filename(
$_
),
@targets
);
if
(
@initial_makefiles
) {
my
$cwd
=
$CWD_INFO
;
foreach
(
@initial_makefiles
) {
Mpp::Makefile::load(
$_
->[0],
$_
->[1], \
%command_line_vars
,
$makecmdgoals
,
\
@include_path
,
$this_ENV
);
}
chdir
$cwd
;
}
{
last
if
@makefiles
||
$CWD_INFO
->{MAKEINFO};
my
$finfo
= Mpp::Makefile::find_makefile_in(
$CWD_INFO
);
$finfo
and
@makefiles
= [
$finfo
,
$CWD_INFO
] and
last
;
$root_makefile
and
last
;
for
(
@targets
) {
$finfo
= Mpp::Makefile::find_makefile_in(
$_
->{
'..'
} ) and
push
@makefiles
, [
$finfo
,
$finfo
->{
'..'
}];
}
@makefiles
or
@makefiles
= [
$CWD_INFO
,
$CWD_INFO
];
}
$Mpp::Recursive::depth
=
$Mpp::Recursive::depth
=
$command_line_vars
{recursive_makepp}
if
defined
$Mpp::Recursive::traditional
||
defined
$Mpp::Recursive::hybrid
;
foreach
(
@makefiles
) {
$_
= Mpp::Makefile::load(
$_
->[0],
$_
->[1], \
%command_line_vars
,
$makecmdgoals
,
\
@include_path
,
$this_ENV
);
}
@targets
or
@targets
= (
$target_cwd
->{MAKEINFO} ||
$makefiles
[0] ||
$CWD_INFO
)->{FIRST_TARGET} ||
die
'no targets specified and no default target in makefile `'
.
absolute_filename( (
$target_cwd
->{MAKEINFO} ||
$makefiles
[0])->{MAKEFILE} ||
$CWD_INFO
) .
"'\n"
;
@targets
;
}
my
@other_args
;
if
(
$MAKEPPFLAGS
||
$MAKEFLAGS
) {
unshift
@ARGV
, unquote_split_on_whitespace
$MAKEPPFLAGS
||
$MAKEFLAGS
;
$ARGV
[0] =~ /^-/ ||
$ARGV
[0] =~ /=/ or
substr
$ARGV
[0], 0, 0,
'-'
;
}
undef
$MAKEFLAGS
;
unshift
@ARGV
, unquote_split_on_whitespace
$ENV
{_MAKEPPFLAGS}
if
$ENV
{_MAKEPPFLAGS};
perform
eval
{
my
$tmp
;
parse_command_line
%global_ENV
,
[
'b'
,
qr/build[-_]?cache/
, \
$tmp
, 1,
sub
{
Mpp::Subs::s_build_cache
$tmp
,
undef
,
$progname
, {
global
=> 1};
}],
[
undef
,
qr/build[-_]?check(?:[-_]?method)?/
, \
$tmp
, 1,
sub
{
Mpp::Subs::s_build_check
$tmp
,
undef
,
$progname
, {
global
=> 1};
}],
[
undef
,
qr/do[-_]?build/
, \
$tmp
, 1,
sub
{
$tmp
= file_info
$tmp
;
undef
$tmp
->{DONT_BUILD};
push
@do_build
,
$tmp
;
}],
[
undef
,
qr/do[-_]?read/
, \
$tmp
, 1,
sub
{
undef
file_info(
$tmp
)->{DONT_READ};
}],
[
undef
,
qr/dont[-_]?build/
, \
$tmp
, 1,
sub
{
$tmp
= file_info
$tmp
;
$tmp
->{DONT_BUILD} = 1;
$dont_build_dir_flag
= 1
if
is_or_will_be_dir
$tmp
;
}],
[
undef
,
qr/dont[-_]?read/
, \
$tmp
, 1,
sub
{
$tmp
= file_info
$tmp
;
$tmp
->{DONT_READ} = 1;
our
$dont_read_dir_flag
= 1
if
is_or_will_be_dir
$tmp
;
}],
[
undef
,
qr/final[-_]?rules?[-_]?only/
, \
$final_rule_only
],
[
undef
,
qr/force[-_]?copy[-_]?from[-_]?bc/
,
eval
'\$Mpp::BuildCache::force_copy'
],
[
undef
,
qr/force[-_]?rescan/
, \
our
$force_rescan
],
[
undef
,
'gullible'
, \
our
$gullible
],
[
undef
,
qr/hybrid(?:[-_]?recurs(?:ion|ive|ive[-_]?make))?/
, \
$Mpp::Recursive::hybrid
],
[
undef
,
qr/implicit[-_]load[-_]?[Mm]akeppfile[-_]?only/
, \
our
$implicit_load_makeppfile_only
],
[
undef
,
qr/(?:in(?:side)?[-_]?)?sand[-_]?box/
, \
$tmp
, 1,
sub
{
$tmp
= file_info
$tmp
;
$tmp
->{IN_SANDBOX} = 1;
our
$sandbox_enabled_flag
= 1;
our
$in_sandbox_dir_flag
= 1
if
is_or_will_be_dir
$tmp
;
}],
[
qw(j jobs)
, \
$Mpp::Event::max_proc
, 1,
sub
{
$Mpp::Event::max_proc
=~ /^\d+$/ or
die
"$progname: invalid argument to -j\n"
;
if
( is_windows > 0 ) {
$Mpp::Event::max_proc
= 1;
warn
"parallel make (-j) only supported on Cygwin or MinGW Perl\n"
;
return
;
}
if
(
$Mpp::Event::max_proc
!= 1 ) {
$Mpp::parallel_make
= 1;
}
}],
[
undef
,
qr/last[-_]?chance[-_]?rules/
, \
our
$last_chance_rules
],
[
'm'
,
qr/signature(?:[-_]?method)?/
, \
$tmp
, 1,
sub
{
Mpp::Subs::s_signature
$tmp
,
undef
,
$progname
, {
global
=> 1};
}],
[
undef
,
qr/override[-_]?signature(?:[-_]?method)?/
, \
$tmp
, 1,
sub
{
Mpp::Subs::s_signature
$tmp
,
undef
,
$progname
, {
global
=> 1,
override
=> 1};
}],
[
undef
,
qr/md5[-_]?(?:check[-_]?)?bc/
,
eval
'\$Mpp::BuildCache::md5check'
],
[
undef
,
qr/no[-_]?cache[-_]?scaninfos?/
, \
our
$nocache_scaninfo
],
[
undef
,
qr/no[-_]?implicit[-_]?load/
, \
$implicitly_load_makefiles
,
undef
, 0],
[
undef
,
qr/no[-_]?path[-_]?exe(?:cutable)?[-_]?dep(?:s|endency|endencies)?/
, \
our
$no_path_executable_dependencies
],
[
undef
,
qr/no[-_]?populate[-_]?bc/
, \
$Mpp::BuildCache::read_only
],
[
undef
,
qr/no[-_]?remake[-_]?makefiles/
, \
$remake_makefiles
,
undef
, 0],
[
undef
,
qr/out[-_]?of[-_]?sand[-_]?box/
, \
$tmp
, 1,
sub
{
$tmp
= file_info
$tmp
;
undef
$tmp
->{IN_SANDBOX};
our
$in_sandbox_dir_flag
= 1
if
is_or_will_be_dir
$tmp
;
}],
[
undef
,
qr/populate[-_]?bc[-_]?only/
, \
$Mpp::BuildCache::write_only
],
[
undef
,
qr/re?m(?:ove)?[-_]?stale(?:[-_]?files)?/
, \
$rm_stale_files
],
[
undef
,
qr/sand[-_]?box[-_]?warn(?:ing)?/
, \
$sandbox_warn_flag
],
[
undef
,
qr/loop|stop(?:[-_]?before[-_]?build(?:ing)?|[-_]?after[-_]?load(?:ing|ed)?)?/
, \
$loop
],
[
undef
,
qr/stop[-_]?(?:on[-_]?)?race/
, \
$stop_on_race
],
[
undef
,
qr/symlink[-_]?in[-_]?rep(?:os(?:itory)?)?[-_]?as[-_]?file/
,
eval
'\$Mpp::Repository::symlink_as_file'
],
[
undef
,
qr/traditional(?:[-_]?recurs(?:ion|ive|ive[-_]?make))?/
, \
$Mpp::Recursive::traditional
],
[
undef
,
qr/virtual[-_]?sandbox/
, \
$virtual_sandbox_flag
],
[
undef
,
qr/warn[-_]?undef(?:ined)?(?:[-_]?var(?:iables)?)?/
, \
$warn_undef_var
];
};