📄 path.jam
字号:
# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
# distribute this software is granted provided this copyright notice appears in
# all copies. This software is provided "as is" without express or implied
# warranty, and with no claim as to its suitability for any purpose.
# Performs various path manipulations. Path are always in a 'normilized'
# representation. In it, a path may be either:
#
# - '.', or
#
# - ['/'] [ ( '..' '/' )* (token '/')* token ]
#
# In plain english, path can be rooted, '..' elements are allowed only
# at the beginning, and it never ends in slash, except for path consisting
# of slash only.
import modules ;
import sequence ;
import regex ;
import errors : error ;
os = [ modules.peek : OS ] ;
if [ modules.peek : UNIX ]
{
local uname = [ modules.peek : JAMUNAME ] ;
switch $(uname)
{
case CYGWIN* :
os = CYGWIN ;
case * :
os = UNIX ;
}
}
#
# Converts the native path into normalized form.
#
rule make ( native )
{
return [ make-$(os) $(native) ] ;
}
#
# Builds native representation of the path.
#
rule native ( path )
{
return [ native-$(os) $(path) ] ;
}
#
# Tests if a path is rooted.
#
rule is-rooted ( path )
{
return [ MATCH "^(/)" : $(path) ] ;
}
#
# Tests if a path has a parent.
#
rule has-parent ( path )
{
if $(path) != / {
return 1 ;
} else {
return ;
}
}
#
# Returns the path without any directory components.
#
rule basename ( path )
{
return [ MATCH "([^/]+)$" : $(path) ] ;
}
#
# Returns parent directory of the path. If no parent exists, error is issued.
#
rule parent ( path )
{
if [ has-parent $(path) ] {
if $(path) = . {
return .. ;
} else {
# Strip everything at the end of path up to and including
# the last slash
local result = [ regex.match "((.*)/)?([^/]+)" : $(path) : 2 3 ] ;
# Did we strip what we shouldn't?
if $(result[2]) = ".." {
return $(path)/.. ;
} else {
if ! $(result[1]) {
if [ is-rooted $(path) ] {
result = / ;
} else {
result = . ;
}
}
return $(result[1]) ;
}
}
} else {
error "Path '$(path)' has no parent" ;
}
}
#
# Returns path2 such that "[ join path path2 ] = .".
# The path may not contain ".." element or be rooted.
#
rule reverse ( path )
{
if $(path) = .
{
return $(path) ;
}
else
{
local tokens = [ regex.split $(path) "/" ] ;
local tokens2 ;
for local i in $(tokens) {
tokens2 += .. ;
}
return [ sequence.join $(tokens2) : "/" ] ;
}
}
#
# Auxillary rule: does all the semantic of 'join', except for error cheching.
# The error checking is separated because this rule is recursive, and I don't
# like the idea of checking the same input over and over.
#
local rule join-imp ( elements + )
{
return [ NORMALIZE_PATH $(elements:J="/") ] ;
}
#
# Contanenates the passed path elements. Generates an error if
# any element other than the first one is rooted.
#
rule join ( elements + )
{
if ! $(elements[2])
{
return $(elements[1]) ;
}
else
{
for local e in $(elements[2-])
{
if [ is-rooted $(e) ]
{
error only first element may be rooted ;
}
}
return [ join-imp $(elements) ] ;
}
}
#
# If 'path' is relative, it is rooted at 'root'. Otherwise, it's unchanged.
#
rule root ( path root )
{
if [ is-rooted $(path) ] {
return $(path) ;
} else {
return [ join $(root) $(path) ] ;
}
}
#
# Returns the current working directory.
#
rule pwd ( )
{
return [ make [ PWD ] ] ;
}
#
# Returns the list of files matching the given pattern in the
# specified directory. Both directories and patterns are
# supplied as portable paths. Each pattern should be non-absolute
# path, and can't contain "." or ".." elements. Each slash separated
# element of pattern can contain the following special characters:
# - '?', which match any character
# - '*', which matches arbitrary number of characters.
# A file $(d)/e1/e2/e3 (where 'd' is in $(dirs)) matches pattern p1/p2/p3
# if and only if e1 matches p1, e2 matches p2 and so on.
#
# For example:
# [ glob . : *.cpp ]
# [ glob . : */build/Jamfile ]
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) ] ;
real-patterns += [ path.native $(pattern) ] ;
}
}
return [ sequence.transform path.make :
[ GLOB-RECURSIVELY $(real-patterns) ] ] ;
}
#
# Returns true is the specified file exists.
#
rule exists ( file )
{
return [ path.glob $(file:D) : $(file:D=) ] ;
}
NATIVE_RULE path : exists ;
#
# Find out the absolute name of path and returns the list of all the parents,
# starting with the immediate one. Parents are returned as relative names.
# If 'upper_limit' is specified, directories above it will be pruned.
#
rule all-parents ( path : upper_limit ? : cwd ? )
{
cwd ?= [ pwd ] ;
local path_ele = [ regex.split [ root $(path) $(cwd) ] "/" ] ;
if ! $(upper_limit) {
upper_limit = / ;
}
local upper_ele = [ regex.split [ root $(upper_limit) $(cwd) ] "/" ] ;
# Leave only elements in 'path_ele' below 'upper_ele'
while $(path_ele) && $(upper_ele[1]) = $(path_ele[1]) {
upper_ele = $(upper_ele[2-]) ;
path_ele = $(path_ele[2-]) ;
}
# All upper elements removed ?
if ! $(upper_ele) {
# Create the relative paths to parents, number of elements in 'path_ele'
local result ;
for local i in $(path_ele) {
path = [ parent $(path) ] ;
result += $(path) ;
}
return $(result) ;
}
else {
error "$(upper_limit) is not prefix of $(path)" ;
}
}
#
# Search for 'pattern' in parent directories of 'dir', up till and including
# 'upper_limit', if it is specified, or till the filesystem root otherwise.
#
rule glob-in-parents ( dir : patterns + : upper-limit ? )
{
local result ;
local parent-dirs = [ all-parents $(dir) : $(upper-limit) ] ;
while $(parent-dirs) && ! $(result)
{
result = [ glob $(parent-dirs[1]) : $(patterns) ] ;
parent-dirs = $(parent-dirs[2-]) ;
}
return $(result) ;
}
#
# Assuming 'child' is a subdirectory of 'parent', return the relative
# path from 'parent' to 'child'
#
rule relative ( child parent )
{
if $(parent) = "."
{
return $(child) ;
}
else
{
local split1 = [ regex.split $(parent) / ] ;
local split2 = [ regex.split $(child) / ] ;
while $(split1)
{
if $(split1[1]) = $(split2[1])
{
split1 = $(split1[2-]) ;
split2 = $(split2[2-]) ;
}
else
{
errors.error $(child) is not a subdir of $(parent) ;
}
}
return [ join $(split2) ] ;
}
}
# Returns the minimal path to path2 that is relative path1.
#
rule relative-to ( path1 path2 )
{
local root_1 = [ regex.split [ reverse $(path1) ] / ] ;
local split1 = [ regex.split $(path1) / ] ;
local split2 = [ regex.split $(path2) / ] ;
while $(split1) && $(root_1)
{
if $(split1[1]) = $(split2[1])
{
root_1 = $(root_1[2-]) ;
split1 = $(split1[2-]) ;
split2 = $(split2[2-]) ;
}
else
{
split1 = ;
}
}
return [ join . $(root_1) $(split2) ] ;
}
# Returns the list of paths which are used by the operating system
# for looking up programs
rule programs-path ( )
{
local result ;
local raw = [ modules.peek : PATH Path path ] ;
for local p in $(raw)
{
if $(p)
{
result += [ path.make $(p) ] ;
}
}
return $(result) ;
}
rule make-NT ( native )
{
local tokens = [ regex.split $(native) "[/\\]" ] ;
local result ;
# Handle paths ending with slashes
if $(tokens[-1]) = ""
{
tokens = $(tokens[1--2]) ; # discard the empty element
}
result = [ path.join $(tokens) ] ;
if [ regex.match "(^.:)" : $(native) ]
{
result = /$(result) ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -