diff --git a/src/build/project.jam b/src/build/project.jam index 8e25573b0..9c053839c 100644 --- a/src/build/project.jam +++ b/src/build/project.jam @@ -94,7 +94,10 @@ rule load ( jamfile-location ) return $(module-name) ; } -JAMROOT = project-root.jam Jamroot jamroot Jamroot.jam jamroot.jam ; +# Note the use of character groups, as opposed to listing +# 'Jamroot' and 'jamroot'. With the latter, we'd get duplicate +# matches on windows and would have to eliminate duplicates. +JAMROOT = project-root.jam [Jj]amroot [Jj]amroot.jam ; # Loads parent of Jamfile at 'location'. Issues an error if nothing is found. rule load-parent ( location ) { @@ -301,7 +304,7 @@ local rule load-jamfile ( .jamfile-modules += $(jamfile-module) ; mark-as-user $(jamfile-module) ; modules.load $(jamfile-module) : [ path.native $(jamfile-to-load) ] : . ; - if $(jamfile-to-load:BS) in $(JAMROOT) + if $(jamfile-to-load:BS) = project-root.jam { jamfile = [ find-jamfile $(dir) : no-errors ] ; if $(jamfile) @@ -430,7 +433,8 @@ rule initialize ( # We search for parent/project-root only if jamfile was specified # --- i.e # if the project is not standalone. - if $(location) && ! $(basename) in $(JAMROOT) + if $(location) && ! $(basename) in + project-root.jam Jamroot jamroot Jamroot.jam Jamroot { parent-module = [ load-parent $(location) ] ; } @@ -889,11 +893,13 @@ module project-rules { import path ; import project ; + import sequence ; local location = [ project.attribute $(__name__) source-location ] ; local result ; - local paths = [ path.glob $(location) : $(wildcards) ] ; + local paths = [ path.glob $(location) : + [ sequence.transform path.make : $(wildcards) ] ] ; if $(wildcards:D) { # The paths we've found are relative to current directory, diff --git a/src/engine/builtins.c b/src/engine/builtins.c index 899c7ccce..5ef7e557f 100644 --- a/src/engine/builtins.c +++ b/src/engine/builtins.c @@ -110,6 +110,12 @@ load_builtins() ); } + { + char * args[] = { "patterns", "*", 0 }; + bind_builtin( "GLOB-RECURSIVELY" , builtin_glob_recursive, 0, args ); + } + + duplicate_rule( "Includes" , bind_builtin( "INCLUDES" , builtin_depends, 1, 0 ) ); @@ -550,8 +556,9 @@ builtin_glob_back( string_new( buf ); path_build( &f, buf, 0 ); - if (globbing->case_insensitive) + if (globbing->case_insensitive) { downcase_inplace( buf->value ); + } for( l = globbing->patterns; l; l = l->next ) { @@ -619,6 +626,160 @@ builtin_glob( return globbing.results; } +static int has_wildcards(const char* str) +{ + size_t index = strcspn(str, "[]*?"); + if (str[index] == '\0') + return 0; + else + return 1; +} + +/** If 'file' exists, append 'file' to 'list'. + Returns 'list'. +*/ +static LIST* append_if_exists(LIST* list, char* file) +{ + time_t time; + timestamp(file, &time); + if (time > 0) + return list_new(list, newstr(file)); + else + return list; +} + +LIST* glob1(char* dirname, char* pattern) +{ + LIST* plist = list_new(L0, pattern); + struct globbing globbing; + + globbing.results = L0; + globbing.patterns = plist; + + globbing.case_insensitive +# if defined( OS_NT ) || defined( OS_CYGWIN ) + = plist; /* always case-insensitive if any files can be found */ +# else + = L0; +# endif + + if ( globbing.case_insensitive ) + { + globbing.patterns = downcase_list( plist ); + } + + file_dirscan( dirname, builtin_glob_back, &globbing ); + + if ( globbing.case_insensitive ) + { + list_free( globbing.patterns ); + } + + list_free(plist); + + return globbing.results; +} + + +LIST* glob_recursive(char* pattern) +{ + LIST* result = L0; + + /* Check if there's metacharacters in pattern */ + if (!has_wildcards(pattern)) + { + /* No metacharacters. Check if the path exists. */ + result = append_if_exists(result, pattern); + } + else + { + /* Have metacharacters in the pattern. Split into dir/name */ + PATHNAME path[1]; + path_parse(pattern, path); + + if (path->f_dir.ptr) + { + LIST* dirs = L0; + string dirname[1]; + string basename[1]; + string_new(dirname); + string_new(basename); + + string_append_range(dirname, path->f_dir.ptr, + path->f_dir.ptr + path->f_dir.len); + + path->f_grist.ptr = 0; + path->f_grist.len = 0; + path->f_dir.ptr = 0; + path->f_dir.len = 0; + path_build(path, basename, 0); + + if (has_wildcards(dirname->value)) + { + dirs = glob_recursive(dirname->value); + } + else + { + dirs = list_new(dirs, dirname->value); + } + + if (has_wildcards(basename->value)) + { + for(; dirs; dirs = dirs->next) + { + result = list_append(result, + glob1(dirs->string, basename->value)); + } + } + else + { + string file_string[1]; + string_new(file_string); + + /** No wildcard in basename. */ + for(; dirs; dirs = dirs->next) + { + path->f_dir.ptr = dirs->string; + path->f_dir.len = strlen(dirs->string); + path_build(path, file_string, 0); + + result = append_if_exists(result, file_string->value); + + string_truncate(file_string, 0); + } + + string_free(file_string); + } + + string_free(dirname); + string_free(basename); + } + else + { + /** No directory, just a pattern. */ + result = list_append(result, glob1(".", pattern)); + } + } + + return result; +} + +LIST * +builtin_glob_recursive( + PARSE *parse, + FRAME *frame ) +{ + LIST* result = L0; + LIST* l = lol_get( frame->args, 0 ); + + for(; l; l = l->next) + { + result = list_append(result, glob_recursive(l->string)); + } + + return result; +} + /* * builtin_match() - MATCH rule, regexp matching */ diff --git a/src/engine/builtins.h b/src/engine/builtins.h index 781a21943..72404406f 100644 --- a/src/engine/builtins.h +++ b/src/engine/builtins.h @@ -22,6 +22,7 @@ LIST *builtin_echo( PARSE *parse, FRAME *args ); LIST *builtin_exit( PARSE *parse, FRAME *args ); LIST *builtin_flags( PARSE *parse, FRAME *args ); LIST *builtin_glob( PARSE *parse, FRAME *args ); +LIST *builtin_glob_recursive( PARSE *parse, FRAME *frame ); LIST *builtin_subst( PARSE *parse, FRAME *args ); LIST *builtin_match( PARSE *parse, FRAME *args ); LIST *builtin_hdrmacro( PARSE *parse, FRAME *args ); diff --git a/src/kernel/bootstrap.jam b/src/kernel/bootstrap.jam index 1bbbb04b8..bce3374c3 100755 --- a/src/kernel/bootstrap.jam +++ b/src/kernel/bootstrap.jam @@ -12,6 +12,21 @@ if $(JAM_VERSION:J="") < 030109 EXIT ; } +local required-rules = GLOB-RECURSIVELY ; + +for local r in $(required-rules) +{ + if ! $(r) in [ RULENAMES ] + { + ECHO "error: builtin rule '$(r)' is not present" ; + ECHO "error: you version of bjam is likely out of date" ; + ECHO "error: please get a fresh version from CVS." ; + EXIT ; + } +} + + + # Bootstrap the module system. Then bring the import rule into the global module. # SEARCH on modules.jam = $(.bootstrap-file:D) ; diff --git a/src/util/path.jam b/src/util/path.jam index 567b0896c..781eb50fc 100644 --- a/src/util/path.jam +++ b/src/util/path.jam @@ -204,97 +204,23 @@ rule pwd ( ) rule glob ( dirs * : patterns + ) { local result ; + local real-patterns ; for local d in $(dirs) { for local p in $(patterns) { local pattern = [ path.root $(p) $(d) ] ; - result += [ glob-really [ path.native $(pattern) ] ] ; + real-patterns += [ path.native $(pattern) ] ; } } # Windows is not case-sensitive, so if you're globbing # for Jamroot and jamroot, the result will include 'Jamroot' # twice. Remove duplicates. - return [ sequence.transform path.make : [ sequence.unique $(result) ] ] ; + return [ sequence.unique [ sequence.transform path.make : + [ GLOB-RECURSIVELY $(real-patterns) ] ] ] ; } -# Looks for file matching 'pattern' in 'directory'. -# Does not recurse. Directory is in the native format. -rule glob-one ( dir : p ) -{ - local result ; - # When a pattern has not directory, we glob directly. - # Take care of special ".." value. The "GLOB" rule simply ignores - # the ".." element (and ".") element in directory listings. This is - # needed so that - # - # [ glob libs/*/Jamfile ] - # - # don't return - # - # libs/../Jamfile (which is the same as ./Jamfile) - # - # On the other hand, when ".." is explicitly present in the pattern - # we need to return it. - # - - if $(p) != ".." - { - # On cygwin, 'cygdrive' is a special directory and is - # not shown when listing '/'. - if $(dir) = / && $(p) = cygdrive - { - result = /cygdrive ; - } - else - { - result += [ GLOB $(dir) : $(p) ] ; - } - } - else - { - result += [ path.join $(dir) .. ] ; - } - return $(result) ; -} - -# Searches for a single pattern and returns matches. -# Both pattern and result are in native format. -rule glob-really ( pattern ) -{ - local result ; - # The second condition protects again looping infinitely - # on '/' path. - if $(pattern:D) && $(pattern:D) != $(pattern) - { - # When the pattern has a directory element, we first glob for - # directory, and then glob for file name is the found directories. - - # First glob for directory part. - local globbed-dirs = [ glob-really $(pattern:D) ] ; - for local dir in $(globbed-dirs) - { - local x = [ glob-one $(dir) : $(pattern:D="") ] ; - result += $(x) ; - } - } - else - { - if $(pattern:D) = $(pattern) - { - # Just return the result. - # FIXME: Maybe need to check that the directory - # is indeed present. - result = $(pattern) ; - } - else - { - result = [ glob-one . : $(pattern) ] ; - } - } - return $(result) ; -} # # Returns true is the specified file exists. diff --git a/test/startup_v1.py b/test/startup_v1.py index ba671d7a9..2d80c8a02 100644 --- a/test/startup_v1.py +++ b/test/startup_v1.py @@ -20,9 +20,9 @@ t = Tester( t.set_tree('startup') -if os.name == 'nt': - t.run_build_system( - status=1, stdout="You didn't set BOOST_ROOT", match = expect_substring) +#if os.name == 'nt': +# t.run_build_system( +# status=1, stdout="You didn't set BOOST_ROOT", match = expect_substring) t.run_build_system( extra_args = '-sBOOST_ROOT=.', status=1