mirror of
https://github.com/boostorg/build.git
synced 2026-01-31 20:12:19 +00:00
Revamp path.glob.
The problem with the previous implementation is that it would iterate over all elements in a dir even if 'pattern' had no metacharacters. First, that was slow -- if you handed /cygdrive/w/My Documents/boost/test/a.cpp to it, it would crawl all the way to the top, listing each directory and checking each file. Second, it would fail, because Cygwin is broken and does not show 'cygdrive' is the list of directory elements of '/'. Now we check if a pattern has metacharacters, and if not, just do a simple 'timestamp' call. The new glob is implemented as new 'GLOB-RECURSIVELY' builtin. I've decided to use builtin since otherwise, we'd need 'does this name exist' builtin, and if we need new builtin, why don't implement all globbing in core. [SVN r29163]
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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 <module@>modules.jam = $(.bootstrap-file:D) ;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user