2011-09-10 11:13:51 +02:00
#!/usr/bin/env python
# encoding: utf-8
2018-01-01 20:53:49 +01:00
# Thomas Nagy, 2005-2018 (ita)
2011-09-10 11:13:51 +02:00
"""
Classes related to the build phase ( build , clean , install , step , etc )
The inheritance tree is the following :
"""
2014-06-28 17:08:01 +02:00
import os , sys , errno , re , shutil , stat
2012-02-11 14:31:00 +01:00
try :
import cPickle
except ImportError :
import pickle as cPickle
2016-06-24 13:21:32 +02:00
from waflib import Node , Runner , TaskGen , Utils , ConfigSet , Task , Logs , Options , Context , Errors
2011-09-10 11:13:51 +02:00
CACHE_DIR = ' c4che '
2016-06-24 13:21:32 +02:00
""" Name of the cache directory """
2011-09-10 11:13:51 +02:00
CACHE_SUFFIX = ' _cache.py '
2016-06-24 13:21:32 +02:00
""" ConfigSet cache files for variants are written under :py:attr:´ waflib.Build.CACHE_DIR´ in the form ´ variant_name´ _cache.py """
2011-09-10 11:13:51 +02:00
INSTALL = 1337
""" Positive value ' -> ' install, see :py:attr:`waflib.Build.BuildContext.is_install` """
UNINSTALL = - 1337
""" Negative value ' <- ' uninstall, see :py:attr:`waflib.Build.BuildContext.is_install` """
2015-12-24 18:28:20 +01:00
SAVED_ATTRS = ' root node_sigs task_sigs imp_sigs raw_deps node_deps ' . split ( )
2016-02-14 11:10:38 +01:00
""" Build class members to save between the runs; these should be all dicts
except for ` root ` which represents a : py : class : ` waflib . Node . Node ` instance
"""
2011-09-10 11:13:51 +02:00
CFG_FILES = ' cfg_files '
""" Files from the build directory to hash before starting the build (``config.h`` written during the configuration) """
POST_AT_ONCE = 0
2016-04-30 15:10:16 +02:00
""" Post mode: all task generators are posted before any task executed """
2011-09-10 11:13:51 +02:00
POST_LAZY = 1
2016-04-30 15:10:16 +02:00
""" Post mode: post the task generators group after group, the tasks in the next group are created when the tasks in the previous groups are done """
2011-09-10 11:13:51 +02:00
2015-10-07 01:11:36 +02:00
PROTOCOL = - 1
if sys . platform == ' cli ' :
PROTOCOL = 0
2011-09-10 11:13:51 +02:00
class BuildContext ( Context . Context ) :
''' executes the build '''
cmd = ' build '
variant = ' '
def __init__ ( self , * * kw ) :
super ( BuildContext , self ) . __init__ ( * * kw )
self . is_install = 0
""" Non-zero value when installing or uninstalling file """
self . top_dir = kw . get ( ' top_dir ' , Context . top_dir )
2016-06-24 13:21:32 +02:00
""" See :py:attr:`waflib.Context.top_dir`; prefer :py:attr:`waflib.Build.BuildContext.srcnode` """
self . out_dir = kw . get ( ' out_dir ' , Context . out_dir )
""" See :py:attr:`waflib.Context.out_dir`; prefer :py:attr:`waflib.Build.BuildContext.bldnode` """
2011-09-10 11:13:51 +02:00
self . run_dir = kw . get ( ' run_dir ' , Context . run_dir )
2016-06-24 13:21:32 +02:00
""" See :py:attr:`waflib.Context.run_dir` """
2011-09-10 11:13:51 +02:00
2016-06-24 13:21:32 +02:00
self . launch_dir = Context . launch_dir
""" See :py:attr:`waflib.Context.out_dir`; prefer :py:meth:`waflib.Build.BuildContext.launch_node` """
2011-09-10 11:13:51 +02:00
2016-06-24 13:21:32 +02:00
self . post_mode = POST_LAZY
""" Whether to post the task generators at once or group-by-group (default is group-by-group) """
2011-09-10 11:13:51 +02:00
2016-04-19 22:00:21 +02:00
self . cache_dir = kw . get ( ' cache_dir ' )
2011-09-10 11:13:51 +02:00
if not self . cache_dir :
2014-08-30 13:05:46 +02:00
self . cache_dir = os . path . join ( self . out_dir , CACHE_DIR )
2011-09-10 11:13:51 +02:00
self . all_envs = { }
2016-06-24 13:21:32 +02:00
""" Map names to :py:class:`waflib.ConfigSet.ConfigSet`, the empty string must map to the default environment """
2011-09-10 11:13:51 +02:00
# ======================================= #
# cache variables
2015-12-24 18:28:20 +01:00
self . node_sigs = { }
2016-06-24 13:21:32 +02:00
""" Dict mapping build nodes to task identifier (uid), it indicates whether a task created a particular file (persists across builds) """
2015-12-24 18:28:20 +01:00
2011-09-10 11:13:51 +02:00
self . task_sigs = { }
2016-06-24 13:21:32 +02:00
""" Dict mapping task identifiers (uid) to task signatures (persists across builds) """
2015-12-24 18:28:20 +01:00
self . imp_sigs = { }
2016-06-24 13:21:32 +02:00
""" Dict mapping task identifiers (uid) to implicit task dependencies used for scanning targets (persists across builds) """
2011-09-10 11:13:51 +02:00
self . node_deps = { }
2016-06-24 13:21:32 +02:00
""" Dict mapping task identifiers (uid) to node dependencies found by :py:meth:`waflib.Task.Task.scan` (persists across builds) """
2011-09-10 11:13:51 +02:00
self . raw_deps = { }
2016-06-24 13:21:32 +02:00
""" Dict mapping task identifiers (uid) to custom data returned by :py:meth:`waflib.Task.Task.scan` (persists across builds) """
2011-09-10 11:13:51 +02:00
self . task_gen_cache_names = { }
self . jobs = Options . options . jobs
2016-06-24 13:21:32 +02:00
""" Amount of jobs to run in parallel """
2011-09-10 11:13:51 +02:00
self . targets = Options . options . targets
2019-01-05 12:02:42 +01:00
""" List of targets to build (default: \\ *) """
2016-06-24 13:21:32 +02:00
2011-09-10 11:13:51 +02:00
self . keep = Options . options . keep
2016-06-24 13:21:32 +02:00
""" Whether the build should continue past errors """
2011-09-10 11:13:51 +02:00
self . progress_bar = Options . options . progress_bar
2016-06-24 13:21:32 +02:00
"""
Level of progress status :
2011-09-10 11:13:51 +02:00
2016-06-24 13:21:32 +02:00
0. normal output
1. progress bar
2. IDE output
3. No output at all
"""
2011-09-10 11:13:51 +02:00
# Manual dependencies.
self . deps_man = Utils . defaultdict ( list )
""" Manual dependencies set by :py:meth:`waflib.Build.BuildContext.add_manual_dependency` """
# just the structure here
self . current_group = 0
"""
Current build group
"""
self . groups = [ ]
"""
List containing lists of task generators
"""
2016-06-24 13:21:32 +02:00
2011-09-10 11:13:51 +02:00
self . group_names = { }
"""
Map group names to the group lists . See : py : meth : ` waflib . Build . BuildContext . add_group `
"""
2016-05-21 11:58:47 +02:00
for v in SAVED_ATTRS :
if not hasattr ( self , v ) :
setattr ( self , v , { } )
2011-09-10 11:13:51 +02:00
def get_variant_dir ( self ) :
""" Getter for the variant_dir attribute """
if not self . variant :
return self . out_dir
2017-09-30 18:13:41 +02:00
return os . path . join ( self . out_dir , os . path . normpath ( self . variant ) )
2011-09-10 11:13:51 +02:00
variant_dir = property ( get_variant_dir , None )
def __call__ ( self , * k , * * kw ) :
"""
Create a task generator and add it to the current build group . The following forms are equivalent : :
def build ( bld ) :
tg = bld ( a = 1 , b = 2 )
def build ( bld ) :
tg = bld ( )
tg . a = 1
tg . b = 2
def build ( bld ) :
tg = TaskGen . task_gen ( a = 1 , b = 2 )
bld . add_to_group ( tg , None )
: param group : group name to add the task generator to
: type group : string
"""
kw [ ' bld ' ] = self
ret = TaskGen . task_gen ( * k , * * kw )
self . task_gen_cache_names = { } # reset the cache, each time
2016-04-19 22:00:21 +02:00
self . add_to_group ( ret , group = kw . get ( ' group ' ) )
2011-09-10 11:13:51 +02:00
return ret
2014-01-16 02:03:53 +01:00
2011-09-10 11:13:51 +02:00
def __copy__ ( self ) :
2016-06-24 13:21:32 +02:00
"""
Build contexts cannot be copied
: raises : : py : class : ` waflib . Errors . WafError `
"""
2016-02-28 14:23:54 +01:00
raise Errors . WafError ( ' build contexts cannot be copied ' )
2011-09-10 11:13:51 +02:00
def load_envs ( self ) :
"""
The configuration command creates files of the form ` ` build / c4che / NAMEcache . py ` ` . This method
creates a : py : class : ` waflib . ConfigSet . ConfigSet ` instance for each ` ` NAME ` ` by reading those
2016-06-24 13:21:32 +02:00
files and stores them in : py : attr : ` waflib . Build . BuildContext . allenvs ` .
2011-09-10 11:13:51 +02:00
"""
2011-10-16 08:41:21 +02:00
node = self . root . find_node ( self . cache_dir )
if not node :
raise Errors . WafError ( ' The project was not configured: run " waf configure " first! ' )
2011-12-02 19:13:30 +01:00
lst = node . ant_glob ( ' **/* %s ' % CACHE_SUFFIX , quiet = True )
2011-09-10 11:13:51 +02:00
if not lst :
raise Errors . WafError ( ' The cache directory is empty: reconfigure the project ' )
2011-10-16 08:41:21 +02:00
for x in lst :
name = x . path_from ( node ) . replace ( CACHE_SUFFIX , ' ' ) . replace ( ' \\ ' , ' / ' )
env = ConfigSet . ConfigSet ( x . abspath ( ) )
self . all_envs [ name ] = env
for f in env [ CFG_FILES ] :
newnode = self . root . find_resource ( f )
2016-02-08 22:41:12 +01:00
if not newnode or not newnode . exists ( ) :
raise Errors . WafError ( ' Missing configuration file %r , reconfigure the project! ' % f )
2011-09-10 11:13:51 +02:00
def init_dirs ( self ) :
"""
Initialize the project directory and the build directory by creating the nodes
: py : attr : ` waflib . Build . BuildContext . srcnode ` and : py : attr : ` waflib . Build . BuildContext . bldnode `
2016-06-24 13:21:32 +02:00
corresponding to ` ` top_dir ` ` and ` ` variant_dir ` ` respectively . The ` ` bldnode ` ` directory is
created if necessary .
2011-09-10 11:13:51 +02:00
"""
if not ( os . path . isabs ( self . top_dir ) and os . path . isabs ( self . out_dir ) ) :
raise Errors . WafError ( ' The project was not configured: run " waf configure " first! ' )
self . path = self . srcnode = self . root . find_dir ( self . top_dir )
self . bldnode = self . root . make_node ( self . variant_dir )
self . bldnode . mkdir ( )
def execute ( self ) :
"""
2016-06-24 13:21:32 +02:00
Restore data from previous builds and call : py : meth : ` waflib . Build . BuildContext . execute_build ` .
Overrides from : py : func : ` waflib . Context . Context . execute `
2011-09-10 11:13:51 +02:00
"""
self . restore ( )
if not self . all_envs :
self . load_envs ( )
self . execute_build ( )
def execute_build ( self ) :
"""
Execute the build by :
* reading the scripts ( see : py : meth : ` waflib . Context . Context . recurse ` )
* calling : py : meth : ` waflib . Build . BuildContext . pre_build ` to call user build functions
* calling : py : meth : ` waflib . Build . BuildContext . compile ` to process the tasks
* calling : py : meth : ` waflib . Build . BuildContext . post_build ` to call user build functions
"""
2016-05-28 16:18:51 +02:00
Logs . info ( " Waf: Entering directory ` %s ' " , self . variant_dir )
2011-09-10 11:13:51 +02:00
self . recurse ( [ self . run_dir ] )
self . pre_build ( )
# display the time elapsed in the progress bar
self . timer = Utils . Timer ( )
try :
self . compile ( )
finally :
2014-01-23 22:22:15 +01:00
if self . progress_bar == 1 and sys . stderr . isatty ( ) :
2016-02-15 23:52:11 +01:00
c = self . producer . processed or 1
2014-01-16 02:03:53 +01:00
m = self . progress_line ( c , c , Logs . colors . BLUE , Logs . colors . NORMAL )
Logs . info ( m , extra = { ' stream ' : sys . stderr , ' c1 ' : Logs . colors . cursor_off , ' c2 ' : Logs . colors . cursor_on } )
2016-05-28 16:18:51 +02:00
Logs . info ( " Waf: Leaving directory ` %s ' " , self . variant_dir )
2016-05-13 23:24:02 +02:00
try :
self . producer . bld = None
2016-05-14 00:16:29 +02:00
del self . producer
2016-05-13 23:24:02 +02:00
except AttributeError :
pass
2011-09-10 11:13:51 +02:00
self . post_build ( )
def restore ( self ) :
"""
2016-06-24 13:21:32 +02:00
Load data from a previous run , sets the attributes listed in : py : const : ` waflib . Build . SAVED_ATTRS `
2011-09-10 11:13:51 +02:00
"""
try :
env = ConfigSet . ConfigSet ( os . path . join ( self . cache_dir , ' build.config.py ' ) )
2014-10-30 19:09:53 +01:00
except EnvironmentError :
2011-09-10 11:13:51 +02:00
pass
else :
2016-06-25 23:54:12 +02:00
if env . version < Context . HEXVERSION :
2017-01-30 23:24:35 +01:00
raise Errors . WafError ( ' Project was configured with a different version of Waf, please reconfigure it ' )
2016-06-25 23:54:12 +02:00
for t in env . tools :
2011-09-10 11:13:51 +02:00
self . setup ( * * t )
2013-03-14 13:45:10 +01:00
dbfn = os . path . join ( self . variant_dir , Context . DBFILE )
2011-09-10 11:13:51 +02:00
try :
2013-03-14 13:45:10 +01:00
data = Utils . readf ( dbfn , ' rb ' )
2016-06-05 00:01:01 +02:00
except ( EnvironmentError , EOFError ) :
2013-03-14 13:45:10 +01:00
# handle missing file/empty file
2016-03-19 14:46:22 +01:00
Logs . debug ( ' build: Could not load the build cache %s (missing) ' , dbfn )
2013-03-14 13:45:10 +01:00
else :
2011-09-10 11:13:51 +02:00
try :
2016-06-24 13:21:32 +02:00
Node . pickle_lock . acquire ( )
Node . Nod3 = self . node_class
2011-09-10 11:13:51 +02:00
try :
2013-03-14 13:45:10 +01:00
data = cPickle . loads ( data )
except Exception as e :
2016-03-19 14:46:22 +01:00
Logs . debug ( ' build: Could not pickle the build cache %s : %r ' , dbfn , e )
2013-03-14 13:45:10 +01:00
else :
for x in SAVED_ATTRS :
2016-08-25 18:10:33 +02:00
setattr ( self , x , data . get ( x , { } ) )
2013-03-14 13:45:10 +01:00
finally :
2016-06-24 13:21:32 +02:00
Node . pickle_lock . release ( )
2011-09-10 11:13:51 +02:00
self . init_dirs ( )
def store ( self ) :
"""
2016-06-24 13:21:32 +02:00
Store data for next runs , set the attributes listed in : py : const : ` waflib . Build . SAVED_ATTRS ` . Uses a temporary
2011-09-10 11:13:51 +02:00
file to avoid problems on ctrl + c .
"""
data = { }
for x in SAVED_ATTRS :
data [ x ] = getattr ( self , x )
db = os . path . join ( self . variant_dir , Context . DBFILE )
try :
2016-06-24 13:21:32 +02:00
Node . pickle_lock . acquire ( )
Node . Nod3 = self . node_class
2015-10-07 01:11:36 +02:00
x = cPickle . dumps ( data , PROTOCOL )
2011-09-10 11:13:51 +02:00
finally :
2016-06-24 13:21:32 +02:00
Node . pickle_lock . release ( )
2011-09-10 11:13:51 +02:00
2013-03-14 13:45:10 +01:00
Utils . writef ( db + ' .tmp ' , x , m = ' wb ' )
2011-09-10 11:13:51 +02:00
try :
st = os . stat ( db )
2013-05-18 19:51:45 +02:00
os . remove ( db )
2011-09-10 11:13:51 +02:00
if not Utils . is_win32 : # win32 has no chown but we're paranoid
os . chown ( db + ' .tmp ' , st . st_uid , st . st_gid )
except ( AttributeError , OSError ) :
pass
# do not use shutil.move (copy is not thread-safe)
os . rename ( db + ' .tmp ' , db )
def compile ( self ) :
"""
Run the build by creating an instance of : py : class : ` waflib . Runner . Parallel `
2016-06-24 13:21:32 +02:00
The cache file is written when at least a task was executed .
: raises : : py : class : ` waflib . Errors . BuildError ` in case the build fails
2011-09-10 11:13:51 +02:00
"""
Logs . debug ( ' build: compile() ' )
2016-06-24 13:21:32 +02:00
# delegate the producer-consumer logic to another object to reduce the complexity
2011-09-10 11:13:51 +02:00
self . producer = Runner . Parallel ( self , self . jobs )
self . producer . biter = self . get_build_iterator ( )
try :
self . producer . start ( )
except KeyboardInterrupt :
2017-08-07 08:28:03 +02:00
if self . is_dirty ( ) :
self . store ( )
2011-09-10 11:13:51 +02:00
raise
else :
2017-08-06 10:51:59 +02:00
if self . is_dirty ( ) :
2011-09-10 11:13:51 +02:00
self . store ( )
if self . producer . error :
raise Errors . BuildError ( self . producer . error )
2017-08-06 10:51:59 +02:00
def is_dirty ( self ) :
return self . producer . dirty
2011-09-10 11:13:51 +02:00
def setup ( self , tool , tooldir = None , funs = None ) :
"""
2016-06-24 13:21:32 +02:00
Import waf tools defined during the configuration : :
2011-09-10 11:13:51 +02:00
def configure ( conf ) :
conf . load ( ' glib2 ' )
def build ( bld ) :
pass # glib2 is imported implicitly
: param tool : tool list
: type tool : list
: param tooldir : optional tool directory ( sys . path )
: type tooldir : list of string
: param funs : unused variable
"""
if isinstance ( tool , list ) :
2017-04-17 12:26:47 +02:00
for i in tool :
self . setup ( i , tooldir )
2011-09-10 11:13:51 +02:00
return
2017-11-09 07:15:44 +01:00
module = Context . load_tool ( tool , tooldir )
2017-04-17 12:26:47 +02:00
if hasattr ( module , " setup " ) :
module . setup ( self )
2011-09-10 11:13:51 +02:00
def get_env ( self ) :
""" Getter for the env property """
try :
return self . all_envs [ self . variant ]
except KeyError :
return self . all_envs [ ' ' ]
def set_env ( self , val ) :
""" Setter for the env property """
self . all_envs [ self . variant ] = val
env = property ( get_env , set_env )
def add_manual_dependency ( self , path , value ) :
"""
Adds a dependency from a node object to a value : :
def build ( bld ) :
bld . add_manual_dependency (
bld . path . find_resource ( ' wscript ' ) ,
bld . root . find_resource ( ' /etc/fstab ' ) )
: param path : file path
: type path : string or : py : class : ` waflib . Node . Node `
2016-06-24 13:21:32 +02:00
: param value : value to depend
: type value : : py : class : ` waflib . Node . Node ` , byte object , or function returning a byte object
2011-09-10 11:13:51 +02:00
"""
2016-06-04 09:33:13 +02:00
if not path :
raise ValueError ( ' Invalid input path %r ' % path )
2012-03-24 11:37:48 +01:00
2016-06-24 13:21:32 +02:00
if isinstance ( path , Node . Node ) :
2011-09-10 11:13:51 +02:00
node = path
elif os . path . isabs ( path ) :
node = self . root . find_resource ( path )
else :
node = self . path . find_resource ( path )
2016-06-04 09:33:13 +02:00
if not node :
raise ValueError ( ' Could not find the path %r ' % path )
2012-03-24 11:37:48 +01:00
if isinstance ( value , list ) :
2016-02-09 22:03:20 +01:00
self . deps_man [ node ] . extend ( value )
2012-03-24 11:37:48 +01:00
else :
2016-02-09 22:03:20 +01:00
self . deps_man [ node ] . append ( value )
2011-09-10 11:13:51 +02:00
def launch_node ( self ) :
2016-06-24 13:21:32 +02:00
""" Returns the launch directory as a :py:class:`waflib.Node.Node` object (cached) """
2011-09-10 11:13:51 +02:00
try :
# private cache
return self . p_ln
except AttributeError :
self . p_ln = self . root . find_dir ( self . launch_dir )
return self . p_ln
def hash_env_vars ( self , env , vars_lst ) :
"""
2016-06-24 13:21:32 +02:00
Hashes configuration set variables : :
2011-09-10 11:13:51 +02:00
def build ( bld ) :
bld . hash_env_vars ( bld . env , [ ' CXX ' , ' CC ' ] )
2016-06-04 09:33:13 +02:00
This method uses an internal cache .
2011-09-10 11:13:51 +02:00
: param env : Configuration Set
: type env : : py : class : ` waflib . ConfigSet . ConfigSet `
: param vars_lst : list of variables
: type vars_list : list of string
"""
if not env . table :
env = env . parent
if not env :
return Utils . SIG_NIL
idx = str ( id ( env ) ) + str ( vars_lst )
try :
cache = self . cache_env
except AttributeError :
cache = self . cache_env = { }
else :
try :
return self . cache_env [ idx ]
except KeyError :
pass
lst = [ env [ a ] for a in vars_lst ]
2016-06-04 09:33:13 +02:00
cache [ idx ] = ret = Utils . h_list ( lst )
2011-09-10 11:13:51 +02:00
Logs . debug ( ' envhash: %s %r ' , Utils . to_hex ( ret ) , lst )
return ret
def get_tgen_by_name ( self , name ) :
"""
2016-06-24 13:21:32 +02:00
Fetches a task generator by its name or its target attribute ;
the name must be unique in a build : :
2011-09-10 11:13:51 +02:00
def build ( bld ) :
tg = bld ( name = ' foo ' )
tg == bld . get_tgen_by_name ( ' foo ' )
2016-06-24 13:21:32 +02:00
This method use a private internal cache .
: param name : Task generator name
: raises : : py : class : ` waflib . Errors . WafError ` in case there is no task genenerator by that name
2011-09-10 11:13:51 +02:00
"""
cache = self . task_gen_cache_names
if not cache :
# create the index lazily
for g in self . groups :
for tg in g :
try :
cache [ tg . name ] = tg
except AttributeError :
# raised if not a task generator, which should be uncommon
pass
try :
return cache [ name ]
except KeyError :
raise Errors . WafError ( ' Could not find a task generator for the name %r ' % name )
2016-06-24 13:21:32 +02:00
def progress_line ( self , idx , total , col1 , col2 ) :
2011-09-10 11:13:51 +02:00
"""
2016-06-24 13:21:32 +02:00
Computes a progress bar line displayed when running ` ` waf - p ` `
: returns : progress bar line
: rtype : string
2011-09-10 11:13:51 +02:00
"""
2014-01-14 02:31:08 +01:00
if not sys . stderr . isatty ( ) :
return ' '
2011-09-10 11:13:51 +02:00
n = len ( str ( total ) )
Utils . rot_idx + = 1
ind = Utils . rot_chr [ Utils . rot_idx % 4 ]
2016-06-24 13:21:32 +02:00
pc = ( 100. * idx ) / total
2016-06-04 09:33:13 +02:00
fs = " [ %% %d d/ %% d][ %% s %% 2d %% %% %% s][ %s ][ " % ( n , ind )
2016-06-24 13:21:32 +02:00
left = fs % ( idx , total , col1 , pc , col2 )
right = ' ][ %s %s %s ] ' % ( col1 , self . timer , col2 )
2011-09-10 11:13:51 +02:00
cols = Logs . get_term_cols ( ) - len ( left ) - len ( right ) + 2 * len ( col1 ) + 2 * len ( col2 )
2017-04-17 12:26:47 +02:00
if cols < 7 :
cols = 7
2011-09-10 11:13:51 +02:00
2016-06-24 13:21:32 +02:00
ratio = ( ( cols * idx ) / / total ) - 1
2011-09-10 11:13:51 +02:00
bar = ( ' = ' * ratio + ' > ' ) . ljust ( cols )
2014-01-16 02:03:53 +01:00
msg = Logs . indicator % ( left , bar , right )
2011-09-10 11:13:51 +02:00
return msg
def declare_chain ( self , * k , * * kw ) :
"""
2016-06-24 13:21:32 +02:00
Wraps : py : func : ` waflib . TaskGen . declare_chain ` for convenience
2011-09-10 11:13:51 +02:00
"""
return TaskGen . declare_chain ( * k , * * kw )
def pre_build ( self ) :
2016-06-24 13:21:32 +02:00
""" Executes user-defined methods before the build starts, see :py:meth:`waflib.Build.BuildContext.add_pre_fun` """
2011-09-10 11:13:51 +02:00
for m in getattr ( self , ' pre_funs ' , [ ] ) :
m ( self )
def post_build ( self ) :
2016-06-24 13:21:32 +02:00
""" Executes user-defined methods after the build is successful, see :py:meth:`waflib.Build.BuildContext.add_post_fun` """
2011-09-10 11:13:51 +02:00
for m in getattr ( self , ' post_funs ' , [ ] ) :
m ( self )
def add_pre_fun ( self , meth ) :
"""
2016-06-24 13:21:32 +02:00
Binds a callback method to execute after the scripts are read and before the build starts : :
2011-09-10 11:13:51 +02:00
def mycallback ( bld ) :
print ( " Hello, world! " )
def build ( bld ) :
bld . add_pre_fun ( mycallback )
"""
try :
self . pre_funs . append ( meth )
except AttributeError :
self . pre_funs = [ meth ]
def add_post_fun ( self , meth ) :
"""
2016-06-24 13:21:32 +02:00
Binds a callback method to execute immediately after the build is successful : :
2011-09-10 11:13:51 +02:00
def call_ldconfig ( bld ) :
bld . exec_command ( ' /sbin/ldconfig ' )
def build ( bld ) :
if bld . cmd == ' install ' :
bld . add_pre_fun ( call_ldconfig )
"""
try :
self . post_funs . append ( meth )
except AttributeError :
self . post_funs = [ meth ]
def get_group ( self , x ) :
"""
2016-06-24 13:21:32 +02:00
Returns the build group named ` x ` , or the current group if ` x ` is None
2011-09-10 11:13:51 +02:00
: param x : name or number or None
: type x : string , int or None
"""
if not self . groups :
self . add_group ( )
if x is None :
return self . groups [ self . current_group ]
if x in self . group_names :
return self . group_names [ x ]
return self . groups [ x ]
def add_to_group ( self , tgen , group = None ) :
2016-06-24 13:21:32 +02:00
""" Adds a task or a task generator to the build; there is no attempt to remove it if it was already added. """
2017-02-11 15:04:25 +01:00
assert ( isinstance ( tgen , TaskGen . task_gen ) or isinstance ( tgen , Task . Task ) )
2011-09-10 11:13:51 +02:00
tgen . bld = self
self . get_group ( group ) . append ( tgen )
def get_group_name ( self , g ) :
2016-06-04 09:33:13 +02:00
"""
2016-06-24 13:21:32 +02:00
Returns the name of the input build group
2016-06-04 09:33:13 +02:00
: param g : build group object or build group index
: type g : integer or list
: return : name
: rtype : string
"""
2011-09-10 11:13:51 +02:00
if not isinstance ( g , list ) :
g = self . groups [ g ]
for x in self . group_names :
if id ( self . group_names [ x ] ) == id ( g ) :
return x
return ' '
def get_group_idx ( self , tg ) :
"""
2016-06-24 13:21:32 +02:00
Returns the index of the group containing the task generator given as argument : :
2011-09-10 11:13:51 +02:00
def build ( bld ) :
tg = bld ( name = ' nada ' )
0 == bld . get_group_idx ( tg )
: param tg : Task generator object
: type tg : : py : class : ` waflib . TaskGen . task_gen `
2016-06-24 13:21:32 +02:00
: rtype : int
2011-09-10 11:13:51 +02:00
"""
se = id ( tg )
2016-01-06 14:53:18 +01:00
for i , tmp in enumerate ( self . groups ) :
for t in tmp :
2011-09-10 11:13:51 +02:00
if id ( t ) == se :
return i
return None
def add_group ( self , name = None , move = True ) :
"""
2016-06-24 13:21:32 +02:00
Adds a new group of tasks / task generators . By default the new group becomes
the default group for new task generators ( make sure to create build groups in order ) .
2011-09-10 11:13:51 +02:00
: param name : name for this group
: type name : string
2016-06-24 13:21:32 +02:00
: param move : set this new group as default group ( True by default )
2011-09-10 11:13:51 +02:00
: type move : bool
2016-06-24 13:21:32 +02:00
: raises : : py : class : ` waflib . Errors . WafError ` if a group by the name given already exists
2011-09-10 11:13:51 +02:00
"""
if name and name in self . group_names :
2016-06-24 13:21:32 +02:00
raise Errors . WafError ( ' add_group: name %s already present ' , name )
2011-09-10 11:13:51 +02:00
g = [ ]
self . group_names [ name ] = g
self . groups . append ( g )
if move :
self . current_group = len ( self . groups ) - 1
def set_group ( self , idx ) :
"""
2016-06-24 13:21:32 +02:00
Sets the build group at position idx as current so that newly added
task generators are added to this one by default : :
2011-09-10 11:13:51 +02:00
def build ( bld ) :
bld ( rule = ' touch $ {TGT} ' , target = ' foo.txt ' )
bld . add_group ( ) # now the current group is 1
bld ( rule = ' touch $ {TGT} ' , target = ' bar.txt ' )
bld . set_group ( 0 ) # now the current group is 0
bld ( rule = ' touch $ {TGT} ' , target = ' truc.txt ' ) # build truc.txt before bar.txt
: param idx : group name or group index
: type idx : string or int
"""
if isinstance ( idx , str ) :
g = self . group_names [ idx ]
2016-01-06 14:53:18 +01:00
for i , tmp in enumerate ( self . groups ) :
if id ( g ) == id ( tmp ) :
2011-09-10 11:13:51 +02:00
self . current_group = i
2014-10-04 02:02:38 +02:00
break
2011-09-10 11:13:51 +02:00
else :
self . current_group = idx
def total ( self ) :
"""
2016-06-24 13:21:32 +02:00
Approximate task count : this value may be inaccurate if task generators
are posted lazily ( see : py : attr : ` waflib . Build . BuildContext . post_mode ` ) .
2011-09-10 11:13:51 +02:00
The value : py : attr : ` waflib . Runner . Parallel . total ` is updated during the task execution .
2016-06-24 13:21:32 +02:00
: rtype : int
2011-09-10 11:13:51 +02:00
"""
total = 0
for group in self . groups :
for tg in group :
try :
total + = len ( tg . tasks )
except AttributeError :
total + = 1
return total
def get_targets ( self ) :
"""
2017-07-28 10:07:26 +02:00
This method returns a pair containing the index of the last build group to post ,
and the list of task generator objects corresponding to the target names .
This is used internally by : py : meth : ` waflib . Build . BuildContext . get_build_iterator `
to perform partial builds : :
2011-09-10 11:13:51 +02:00
$ waf - - targets = myprogram , myshlib
2017-07-28 10:07:26 +02:00
: return : the minimum build group index , and list of task generators
: rtype : tuple
2011-09-10 11:13:51 +02:00
"""
to_post = [ ]
min_grp = 0
for name in self . targets . split ( ' , ' ) :
tg = self . get_tgen_by_name ( name )
m = self . get_group_idx ( tg )
if m > min_grp :
min_grp = m
to_post = [ tg ]
elif m == min_grp :
to_post . append ( tg )
return ( min_grp , to_post )
2012-04-13 01:24:43 +02:00
def get_all_task_gen ( self ) :
"""
2016-06-24 13:21:32 +02:00
Returns a list of all task generators for troubleshooting purposes .
2012-04-13 01:24:43 +02:00
"""
lst = [ ]
for g in self . groups :
lst . extend ( g )
return lst
2011-09-10 11:13:51 +02:00
def post_group ( self ) :
"""
2017-04-15 15:12:03 +02:00
Post task generators from the group indexed by self . current_group ; used internally
2016-06-24 13:21:32 +02:00
by : py : meth : ` waflib . Build . BuildContext . get_build_iterator `
2011-09-10 11:13:51 +02:00
"""
2017-07-29 09:19:36 +02:00
def tgpost ( tg ) :
try :
f = tg . post
except AttributeError :
pass
else :
f ( )
2011-09-10 11:13:51 +02:00
if self . targets == ' * ' :
2017-04-15 15:12:03 +02:00
for tg in self . groups [ self . current_group ] :
2017-07-29 09:19:36 +02:00
tgpost ( tg )
2011-09-10 11:13:51 +02:00
elif self . targets :
2017-04-15 15:12:03 +02:00
if self . current_group < self . _min_grp :
for tg in self . groups [ self . current_group ] :
2017-07-29 09:19:36 +02:00
tgpost ( tg )
2011-09-10 11:13:51 +02:00
else :
for tg in self . _exact_tg :
tg . post ( )
else :
ln = self . launch_node ( )
2012-04-07 09:59:05 +02:00
if ln . is_child_of ( self . bldnode ) :
Logs . warn ( ' Building from the build directory, forcing --targets=* ' )
2012-04-12 08:30:45 +02:00
ln = self . srcnode
2012-04-07 09:59:05 +02:00
elif not ln . is_child_of ( self . srcnode ) :
2016-05-28 16:18:51 +02:00
Logs . warn ( ' CWD %s is not under %s , forcing --targets=* (run distclean?) ' , ln . abspath ( ) , self . srcnode . abspath ( ) )
2012-04-07 09:59:05 +02:00
ln = self . srcnode
2018-08-24 22:40:23 +02:00
def is_post ( tg , ln ) :
2017-09-21 22:02:38 +02:00
try :
p = tg . path
except AttributeError :
pass
else :
if p . is_child_of ( ln ) :
2018-08-24 22:40:23 +02:00
return True
def is_post_group ( ) :
for i , g in enumerate ( self . groups ) :
if i > self . current_group :
for tg in g :
if is_post ( tg , ln ) :
return True
if self . post_mode == POST_LAZY and ln != self . srcnode :
# partial folder builds require all targets from a previous build group
if is_post_group ( ) :
ln = self . srcnode
for tg in self . groups [ self . current_group ] :
if is_post ( tg , ln ) :
tgpost ( tg )
2011-09-10 11:13:51 +02:00
def get_tasks_group ( self , idx ) :
"""
2016-06-24 13:21:32 +02:00
Returns all task instances for the build group at position idx ,
used internally by : py : meth : ` waflib . Build . BuildContext . get_build_iterator `
2017-02-11 15:04:25 +01:00
: rtype : list of : py : class : ` waflib . Task . Task `
2011-09-10 11:13:51 +02:00
"""
tasks = [ ]
for tg in self . groups [ idx ] :
2012-04-07 10:54:40 +02:00
try :
2011-09-10 11:13:51 +02:00
tasks . extend ( tg . tasks )
2016-06-04 09:33:13 +02:00
except AttributeError : # not a task generator
2012-04-07 10:54:40 +02:00
tasks . append ( tg )
2011-09-10 11:13:51 +02:00
return tasks
def get_build_iterator ( self ) :
"""
2016-06-24 13:21:32 +02:00
Creates a Python generator object that returns lists of tasks that may be processed in parallel .
2011-09-10 11:13:51 +02:00
2017-02-16 07:03:43 +01:00
: return : tasks which can be executed immediately
2017-02-11 15:04:25 +01:00
: rtype : generator returning lists of : py : class : ` waflib . Task . Task `
2011-09-10 11:13:51 +02:00
"""
if self . targets and self . targets != ' * ' :
( self . _min_grp , self . _exact_tg ) = self . get_targets ( )
if self . post_mode != POST_LAZY :
2017-04-22 17:29:00 +02:00
for self . current_group , _ in enumerate ( self . groups ) :
2011-09-10 11:13:51 +02:00
self . post_group ( )
2017-04-22 17:29:00 +02:00
for self . current_group , _ in enumerate ( self . groups ) :
2011-09-10 11:13:51 +02:00
# first post the task generators for the group
if self . post_mode != POST_AT_ONCE :
self . post_group ( )
# then extract the tasks
2017-04-15 15:12:03 +02:00
tasks = self . get_tasks_group ( self . current_group )
2017-04-22 17:17:33 +02:00
2011-09-10 11:13:51 +02:00
# if the constraints are set properly (ext_in/ext_out, before/after)
# the call to set_file_constraints may be removed (can be a 15% penalty on no-op rebuilds)
# (but leave set_file_constraints for the installation step)
#
# if the tasks have only files, set_file_constraints is required but set_precedence_constraints is not necessary
#
Task . set_file_constraints ( tasks )
Task . set_precedence_constraints ( tasks )
self . cur_tasks = tasks
2017-04-20 23:28:39 +02:00
if tasks :
yield tasks
2016-04-28 00:23:54 +02:00
2011-09-10 11:13:51 +02:00
while 1 :
2017-04-20 23:28:39 +02:00
# the build stops once there are no tasks to process
2011-09-10 11:13:51 +02:00
yield [ ]
2016-04-28 00:23:54 +02:00
def install_files ( self , dest , files , * * kw ) :
"""
2016-06-24 13:21:32 +02:00
Creates a task generator to install files on the system : :
2011-09-10 11:13:51 +02:00
2016-04-28 00:23:54 +02:00
def build ( bld ) :
bld . install_files ( ' $ {DATADIR} ' , self . path . find_resource ( ' wscript ' ) )
2012-02-25 16:47:30 +01:00
2016-06-24 13:21:32 +02:00
: param dest : path representing the destination directory
: type dest : : py : class : ` waflib . Node . Node ` or string ( absolute path )
2016-04-28 00:23:54 +02:00
: param files : input files
2016-06-24 13:21:32 +02:00
: type files : list of strings or list of : py : class : ` waflib . Node . Node `
: param env : configuration set to expand * dest *
: type env : : py : class : ` waflib . ConfigSet . ConfigSet `
2016-04-28 00:23:54 +02:00
: param relative_trick : preserve the folder hierarchy when installing whole folders
: type relative_trick : bool
2016-06-24 13:21:32 +02:00
: param cwd : parent node for searching srcfile , when srcfile is not an instance of : py : class : ` waflib . Node . Node `
2016-04-28 00:23:54 +02:00
: type cwd : : py : class : ` waflib . Node . Node `
2016-06-24 13:21:32 +02:00
: param postpone : execute the task immediately to perform the installation ( False by default )
2016-04-28 00:23:54 +02:00
: type postpone : bool
2011-09-10 11:13:51 +02:00
"""
2016-04-28 00:23:54 +02:00
assert ( dest )
2016-05-18 19:43:14 +02:00
tg = self ( features = ' install_task ' , install_to = dest , install_from = files , * * kw )
2016-05-06 15:51:21 +02:00
tg . dest = tg . install_to
2016-04-28 00:23:54 +02:00
tg . type = ' install_files '
if not kw . get ( ' postpone ' , True ) :
tg . post ( )
return tg
2011-09-10 11:13:51 +02:00
2016-04-28 00:23:54 +02:00
def install_as ( self , dest , srcfile , * * kw ) :
2011-09-10 11:13:51 +02:00
"""
2016-06-24 13:21:32 +02:00
Creates a task generator to install a file on the system with a different name : :
2011-09-10 11:13:51 +02:00
2016-04-28 00:23:54 +02:00
def build ( bld ) :
bld . install_as ( ' $ {PREFIX} /bin ' , ' myapp ' , chmod = Utils . O755 )
2011-09-10 11:13:51 +02:00
2016-06-24 13:21:32 +02:00
: param dest : destination file
: type dest : : py : class : ` waflib . Node . Node ` or string ( absolute path )
2016-04-28 00:23:54 +02:00
: param srcfile : input file
2016-06-24 13:21:32 +02:00
: type srcfile : string or : py : class : ` waflib . Node . Node `
: param cwd : parent node for searching srcfile , when srcfile is not an instance of : py : class : ` waflib . Node . Node `
2016-04-28 00:23:54 +02:00
: type cwd : : py : class : ` waflib . Node . Node `
: param env : configuration set for performing substitutions in dest
2016-06-24 13:21:32 +02:00
: type env : : py : class : ` waflib . ConfigSet . ConfigSet `
: param postpone : execute the task immediately to perform the installation ( False by default )
2016-04-28 00:23:54 +02:00
: type postpone : bool
2011-09-10 11:13:51 +02:00
"""
2016-04-28 00:23:54 +02:00
assert ( dest )
2016-05-18 19:43:14 +02:00
tg = self ( features = ' install_task ' , install_to = dest , install_from = srcfile , * * kw )
2016-05-06 15:51:21 +02:00
tg . dest = tg . install_to
2016-04-28 00:23:54 +02:00
tg . type = ' install_as '
if not kw . get ( ' postpone ' , True ) :
tg . post ( )
return tg
2011-09-10 11:13:51 +02:00
2016-04-28 00:23:54 +02:00
def symlink_as ( self , dest , src , * * kw ) :
2011-09-10 11:13:51 +02:00
"""
2016-06-24 13:21:32 +02:00
Creates a task generator to install a symlink : :
2016-04-28 00:23:54 +02:00
def build ( bld ) :
bld . symlink_as ( ' $ {PREFIX} /lib/libfoo.so ' , ' libfoo.so.1.2.3 ' )
: param dest : absolute path of the symlink
2016-06-24 13:21:32 +02:00
: type dest : : py : class : ` waflib . Node . Node ` or string ( absolute path )
2018-07-29 01:46:58 +02:00
: param src : link contents , which is a relative or absolute path which may exist or not
2016-04-28 00:23:54 +02:00
: type src : string
: param env : configuration set for performing substitutions in dest
2016-06-24 13:21:32 +02:00
: type env : : py : class : ` waflib . ConfigSet . ConfigSet `
2016-04-28 00:23:54 +02:00
: param add : add the task created to a build group - set ` ` False ` ` only if the installation task is created after the build has started
: type add : bool
: param postpone : execute the task immediately to perform the installation
: type postpone : bool
: param relative_trick : make the symlink relative ( default : ` ` False ` ` )
: type relative_trick : bool
2011-09-10 11:13:51 +02:00
"""
2016-04-28 00:23:54 +02:00
assert ( dest )
2016-05-18 19:43:14 +02:00
tg = self ( features = ' install_task ' , install_to = dest , install_from = src , * * kw )
2016-05-06 15:51:21 +02:00
tg . dest = tg . install_to
2016-04-28 00:23:54 +02:00
tg . type = ' symlink_as '
tg . link = src
# TODO if add: self.add_to_group(tsk)
if not kw . get ( ' postpone ' , True ) :
tg . post ( )
return tg
2016-05-18 19:43:14 +02:00
@TaskGen.feature ( ' install_task ' )
2016-04-28 00:23:54 +02:00
@TaskGen.before_method ( ' process_rule ' , ' process_source ' )
2016-05-18 19:43:14 +02:00
def process_install_task ( self ) :
2016-06-24 13:21:32 +02:00
""" Creates the installation task for the current task generator; uses :py:func:`waflib.Build.add_install_task` internally. """
2016-05-06 15:51:21 +02:00
self . add_install_task ( * * self . __dict__ )
@TaskGen.taskgen_method
def add_install_task ( self , * * kw ) :
2016-06-24 13:21:32 +02:00
"""
Creates the installation task for the current task generator , and executes it immediately if necessary
: returns : An installation task
: rtype : : py : class : ` waflib . Build . inst `
"""
2016-04-28 00:23:54 +02:00
if not self . bld . is_install :
return
2016-05-06 15:51:21 +02:00
if not kw [ ' install_to ' ] :
2016-04-28 00:23:54 +02:00
return
2016-05-06 15:51:21 +02:00
if kw [ ' type ' ] == ' symlink_as ' and Utils . is_win32 :
if kw . get ( ' win32_install ' ) :
kw [ ' type ' ] = ' install_as '
2016-04-28 00:23:54 +02:00
else :
# just exit
return
tsk = self . install_task = self . create_task ( ' inst ' )
2016-05-06 15:51:21 +02:00
tsk . chmod = kw . get ( ' chmod ' , Utils . O644 )
2016-05-17 19:49:44 +02:00
tsk . link = kw . get ( ' link ' , ' ' ) or kw . get ( ' install_from ' , ' ' )
2016-05-06 15:51:21 +02:00
tsk . relative_trick = kw . get ( ' relative_trick ' , False )
tsk . type = kw [ ' type ' ]
2016-05-18 19:43:14 +02:00
tsk . install_to = tsk . dest = kw [ ' install_to ' ]
2016-05-06 15:51:21 +02:00
tsk . install_from = kw [ ' install_from ' ]
2016-05-20 19:20:54 +02:00
tsk . relative_base = kw . get ( ' cwd ' ) or kw . get ( ' relative_base ' , self . path )
2016-10-08 22:35:05 +02:00
tsk . install_user = kw . get ( ' install_user ' )
tsk . install_group = kw . get ( ' install_group ' )
2016-05-06 11:12:54 +02:00
tsk . init_files ( )
2016-05-06 15:51:21 +02:00
if not kw . get ( ' postpone ' , True ) :
2016-05-06 09:57:01 +02:00
tsk . run_now ( )
2016-05-06 15:51:21 +02:00
return tsk
@TaskGen.taskgen_method
def add_install_files ( self , * * kw ) :
2016-06-24 13:21:32 +02:00
"""
Creates an installation task for files
: returns : An installation task
: rtype : : py : class : ` waflib . Build . inst `
"""
2016-05-06 15:51:21 +02:00
kw [ ' type ' ] = ' install_files '
return self . add_install_task ( * * kw )
@TaskGen.taskgen_method
def add_install_as ( self , * * kw ) :
2016-06-24 13:21:32 +02:00
"""
Creates an installation task for a single file
: returns : An installation task
: rtype : : py : class : ` waflib . Build . inst `
"""
2016-05-06 15:51:21 +02:00
kw [ ' type ' ] = ' install_as '
return self . add_install_task ( * * kw )
@TaskGen.taskgen_method
def add_symlink_as ( self , * * kw ) :
2016-06-24 13:21:32 +02:00
"""
Creates an installation task for a symbolic link
: returns : An installation task
: rtype : : py : class : ` waflib . Build . inst `
"""
2016-05-06 15:51:21 +02:00
kw [ ' type ' ] = ' symlink_as '
return self . add_install_task ( * * kw )
2016-04-28 00:23:54 +02:00
class inst ( Task . Task ) :
2016-06-24 13:21:32 +02:00
""" Task that installs files or symlinks; it is typically executed by :py:class:`waflib.Build.InstallContext` and :py:class:`waflib.Build.UnInstallContext` """
2016-04-28 00:23:54 +02:00
def __str__ ( self ) :
2016-06-24 13:21:32 +02:00
""" Returns an empty string to disable the standard task display """
2016-04-28 00:23:54 +02:00
return ' '
def uid ( self ) :
2016-06-24 13:21:32 +02:00
""" Returns a unique identifier for the task """
2016-04-28 00:23:54 +02:00
lst = self . inputs + self . outputs + [ self . link , self . generator . path . abspath ( ) ]
return Utils . h_list ( lst )
2011-09-10 11:13:51 +02:00
2016-05-06 11:12:54 +02:00
def init_files ( self ) :
2016-06-24 13:21:32 +02:00
"""
Initializes the task input and output nodes
"""
2016-05-06 11:12:54 +02:00
if self . type == ' symlink_as ' :
inputs = [ ]
else :
2016-05-06 15:51:21 +02:00
inputs = self . generator . to_nodes ( self . install_from )
2016-05-06 11:12:54 +02:00
if self . type == ' install_as ' :
assert len ( inputs ) == 1
self . set_inputs ( inputs )
dest = self . get_install_path ( )
outputs = [ ]
if self . type == ' symlink_as ' :
if self . relative_trick :
self . link = os . path . relpath ( self . link , os . path . dirname ( dest ) )
outputs . append ( self . generator . bld . root . make_node ( dest ) )
elif self . type == ' install_as ' :
outputs . append ( self . generator . bld . root . make_node ( dest ) )
else :
for y in inputs :
if self . relative_trick :
2016-05-20 19:20:54 +02:00
destfile = os . path . join ( dest , y . path_from ( self . relative_base ) )
2016-05-06 11:12:54 +02:00
else :
destfile = os . path . join ( dest , y . name )
outputs . append ( self . generator . bld . root . make_node ( destfile ) )
self . set_outputs ( outputs )
2016-04-28 00:23:54 +02:00
def runnable_status ( self ) :
2011-09-10 11:13:51 +02:00
"""
2016-04-28 00:23:54 +02:00
Installation tasks are always executed , so this method returns either : py : const : ` waflib . Task . ASK_LATER ` or : py : const : ` waflib . Task . RUN_ME ` .
2011-09-10 11:13:51 +02:00
"""
2016-04-28 00:23:54 +02:00
ret = super ( inst , self ) . runnable_status ( )
if ret == Task . SKIP_ME and self . generator . bld . is_install :
return Task . RUN_ME
return ret
2011-09-10 11:13:51 +02:00
2016-04-28 00:23:54 +02:00
def post_run ( self ) :
2016-06-24 13:21:32 +02:00
"""
Disables any post - run operations
"""
2016-04-28 00:23:54 +02:00
pass
2011-09-10 11:13:51 +02:00
2016-04-28 00:23:54 +02:00
def get_install_path ( self , destdir = True ) :
2016-06-24 13:21:32 +02:00
"""
Returns the destination path where files will be installed , pre - pending ` destdir ` .
2019-02-18 18:38:45 +01:00
2018-09-24 12:05:28 +02:00
Relative paths will be interpreted relative to ` PREFIX ` if no ` destdir ` is given .
2016-06-24 13:21:32 +02:00
: rtype : string
"""
if isinstance ( self . install_to , Node . Node ) :
dest = self . install_to . abspath ( )
else :
2019-02-18 18:38:45 +01:00
dest = os . path . normpath ( Utils . subst_vars ( self . install_to , self . env ) )
2018-09-24 12:05:28 +02:00
if not os . path . isabs ( dest ) :
dest = os . path . join ( self . env . PREFIX , dest )
2016-04-28 00:23:54 +02:00
if destdir and Options . options . destdir :
dest = os . path . join ( Options . options . destdir , os . path . splitdrive ( dest ) [ 1 ] . lstrip ( os . sep ) )
return dest
2011-09-10 11:13:51 +02:00
2016-04-28 00:23:54 +02:00
def copy_fun ( self , src , tgt ) :
2016-06-24 13:21:32 +02:00
"""
2016-10-08 22:35:05 +02:00
Copies a file from src to tgt , preserving permissions and trying to work
around path limitations on Windows platforms . On Unix - like platforms ,
the owner / group of the target file may be set through install_user / install_group
2016-06-24 13:21:32 +02:00
: param src : absolute path
: type src : string
: param tgt : absolute path
: type tgt : string
"""
2014-02-17 00:26:58 +01:00
# override this if you want to strip executables
# kw['tsk'].source is the task that created the files in the build
2014-08-04 15:17:28 +02:00
if Utils . is_win32 and len ( tgt ) > 259 and not tgt . startswith ( ' \\ \\ ? \\ ' ) :
tgt = ' \\ \\ ? \\ ' + tgt
2014-02-17 00:26:58 +01:00
shutil . copy2 ( src , tgt )
2016-10-08 22:35:05 +02:00
self . fix_perms ( tgt )
2014-02-17 00:26:58 +01:00
2016-04-28 00:23:54 +02:00
def rm_empty_dirs ( self , tgt ) :
2016-06-24 13:21:32 +02:00
"""
Removes empty folders recursively when uninstalling .
: param tgt : absolute path
: type tgt : string
"""
2016-04-28 00:23:54 +02:00
while tgt :
tgt = os . path . dirname ( tgt )
try :
os . rmdir ( tgt )
except OSError :
break
def run ( self ) :
2016-06-24 13:21:32 +02:00
"""
Performs file or symlink installation
"""
2016-04-28 00:23:54 +02:00
is_install = self . generator . bld . is_install
if not is_install : # unnecessary?
return
for x in self . outputs :
if is_install == INSTALL :
x . parent . mkdir ( )
2016-05-06 11:12:54 +02:00
if self . type == ' symlink_as ' :
2016-04-28 00:23:54 +02:00
fun = is_install == INSTALL and self . do_link or self . do_unlink
fun ( self . link , self . outputs [ 0 ] . abspath ( ) )
else :
fun = is_install == INSTALL and self . do_install or self . do_uninstall
launch_node = self . generator . bld . launch_node ( )
for x , y in zip ( self . inputs , self . outputs ) :
fun ( x . abspath ( ) , y . abspath ( ) , x . path_from ( launch_node ) )
2016-05-06 09:57:01 +02:00
def run_now ( self ) :
2016-06-24 13:21:32 +02:00
"""
Try executing the installation task right now
: raises : : py : class : ` waflib . Errors . TaskNotReady `
"""
2016-05-06 09:57:01 +02:00
status = self . runnable_status ( )
if status not in ( Task . RUN_ME , Task . SKIP_ME ) :
raise Errors . TaskNotReady ( ' Could not process %r : status %r ' % ( self , status ) )
self . run ( )
self . hasrun = Task . SUCCESS
2016-04-28 00:23:54 +02:00
def do_install ( self , src , tgt , lbl , * * kw ) :
2011-09-10 11:13:51 +02:00
"""
2016-06-24 13:21:32 +02:00
Copies a file from src to tgt with given file permissions . The actual copy is only performed
if the source and target file sizes or timestamps differ . When the copy occurs ,
the file is always first removed and then copied so as to prevent stale inodes .
2011-09-10 11:13:51 +02:00
: param src : file name as absolute path
: type src : string
: param tgt : file destination , as absolute path
: type tgt : string
2016-06-24 13:21:32 +02:00
: param lbl : file source description
: type lbl : string
2011-09-10 11:13:51 +02:00
: param chmod : installation mode
: type chmod : int
2016-06-24 13:21:32 +02:00
: raises : : py : class : ` waflib . Errors . WafError ` if the file cannot be written
2011-09-10 11:13:51 +02:00
"""
if not Options . options . force :
# check if the file is already there to avoid a copy
try :
st1 = os . stat ( tgt )
st2 = os . stat ( src )
except OSError :
pass
else :
# same size and identical timestamps -> make no copy
if st1 . st_mtime + 2 > = st2 . st_mtime and st1 . st_size == st2 . st_size :
2016-04-28 00:23:54 +02:00
if not self . generator . bld . progress_bar :
2019-05-27 21:14:18 +02:00
c1 = Logs . colors . NORMAL
c2 = Logs . colors . BLUE
Logs . info ( ' %s - install %s %s %s (from %s ) ' , c1 , c2 , tgt , c1 , lbl )
2011-09-10 11:13:51 +02:00
return False
2016-04-28 00:23:54 +02:00
if not self . generator . bld . progress_bar :
2019-05-17 07:46:17 +02:00
c1 = Logs . colors . NORMAL
c2 = Logs . colors . BLUE
Logs . info ( ' %s + install %s %s %s (from %s ) ' , c1 , c2 , tgt , c1 , lbl )
2011-09-10 11:13:51 +02:00
2014-06-28 17:08:01 +02:00
# Give best attempt at making destination overwritable,
# like the 'install' utility used by 'make install' does.
try :
2014-07-06 17:41:54 +02:00
os . chmod ( tgt , Utils . O644 | stat . S_IMODE ( os . stat ( tgt ) . st_mode ) )
2014-10-30 19:09:53 +01:00
except EnvironmentError :
2014-06-28 17:08:01 +02:00
pass
2011-09-10 11:13:51 +02:00
# following is for shared libs and stale inodes (-_-)
try :
os . remove ( tgt )
except OSError :
pass
try :
2016-04-28 00:23:54 +02:00
self . copy_fun ( src , tgt )
2016-06-25 13:38:48 +02:00
except EnvironmentError as e :
if not os . path . exists ( src ) :
2016-05-28 16:18:51 +02:00
Logs . error ( ' File %r does not exist ' , src )
2016-06-25 13:38:48 +02:00
elif not os . path . isfile ( src ) :
Logs . error ( ' Input %r is not a file ' , src )
raise Errors . WafError ( ' Could not install the file %r ' % tgt , e )
2011-09-10 11:13:51 +02:00
2016-10-08 22:35:05 +02:00
def fix_perms ( self , tgt ) :
"""
Change the ownership of the file / folder / link pointed by the given path
This looks up for ` install_user ` or ` install_group ` attributes
on the task or on the task generator : :
2016-10-08 23:29:46 +02:00
def build ( bld ) :
bld . install_as ( ' $ {PREFIX} /wscript ' ,
' wscript ' ,
install_user = ' nobody ' , install_group = ' nogroup ' )
bld . symlink_as ( ' $ {PREFIX} /wscript_link ' ,
Utils . subst_vars ( ' $ {PREFIX} /wscript ' , bld . env ) ,
install_user = ' nobody ' , install_group = ' nogroup ' )
2016-10-08 22:35:05 +02:00
"""
if not Utils . is_win32 :
user = getattr ( self , ' install_user ' , None ) or getattr ( self . generator , ' install_user ' , None )
group = getattr ( self , ' install_group ' , None ) or getattr ( self . generator , ' install_group ' , None )
if user or group :
Utils . lchown ( tgt , user or - 1 , group or - 1 )
2016-10-09 21:41:31 +02:00
if not os . path . islink ( tgt ) :
2016-10-08 22:35:05 +02:00
os . chmod ( tgt , self . chmod )
2014-02-17 00:26:58 +01:00
def do_link ( self , src , tgt , * * kw ) :
2011-09-10 11:13:51 +02:00
"""
2016-06-24 13:21:32 +02:00
Creates a symlink from tgt to src .
2011-09-10 11:13:51 +02:00
: param src : file name as absolute path
: type src : string
: param tgt : file destination , as absolute path
: type tgt : string
"""
2016-05-18 21:34:23 +02:00
if os . path . islink ( tgt ) and os . readlink ( tgt ) == src :
if not self . generator . bld . progress_bar :
2019-06-02 09:20:29 +02:00
c1 = Logs . colors . NORMAL
c2 = Logs . colors . BLUE
Logs . info ( ' %s - symlink %s %s %s (to %s ) ' , c1 , c2 , tgt , c1 , src )
2016-05-18 21:34:23 +02:00
else :
2016-04-28 00:23:54 +02:00
try :
os . remove ( tgt )
except OSError :
pass
if not self . generator . bld . progress_bar :
2019-06-02 09:20:29 +02:00
c1 = Logs . colors . NORMAL
c2 = Logs . colors . BLUE
Logs . info ( ' %s + symlink %s %s %s (to %s ) ' , c1 , c2 , tgt , c1 , src )
2011-09-10 11:13:51 +02:00
os . symlink ( src , tgt )
2016-10-08 22:35:05 +02:00
self . fix_perms ( tgt )
2011-09-10 11:13:51 +02:00
2016-04-28 00:23:54 +02:00
def do_uninstall ( self , src , tgt , lbl , * * kw ) :
2016-06-24 13:21:32 +02:00
"""
See : py : meth : ` waflib . Build . inst . do_install `
"""
2016-04-28 00:23:54 +02:00
if not self . generator . bld . progress_bar :
2019-06-02 09:20:29 +02:00
c1 = Logs . colors . NORMAL
c2 = Logs . colors . BLUE
Logs . info ( ' %s - remove %s %s %s ' , c1 , c2 , tgt , c1 )
2011-09-10 11:13:51 +02:00
2016-04-28 00:23:54 +02:00
#self.uninstall.append(tgt)
2011-09-10 11:13:51 +02:00
try :
os . remove ( tgt )
except OSError as e :
if e . errno != errno . ENOENT :
if not getattr ( self , ' uninstall_error ' , None ) :
self . uninstall_error = True
Logs . warn ( ' build: some files could not be uninstalled (retry with -vv to list them) ' )
if Logs . verbose > 1 :
2016-05-28 16:18:51 +02:00
Logs . warn ( ' Could not remove %s (error code %r ) ' , e . filename , e . errno )
2013-09-29 23:45:36 +02:00
self . rm_empty_dirs ( tgt )
2011-09-10 11:13:51 +02:00
2016-04-28 00:23:54 +02:00
def do_unlink ( self , src , tgt , * * kw ) :
2016-06-24 13:21:32 +02:00
"""
See : py : meth : ` waflib . Build . inst . do_link `
"""
2011-09-10 11:13:51 +02:00
try :
2016-04-28 00:23:54 +02:00
if not self . generator . bld . progress_bar :
2019-06-02 09:20:29 +02:00
c1 = Logs . colors . NORMAL
c2 = Logs . colors . BLUE
Logs . info ( ' %s - remove %s %s %s ' , c1 , c2 , tgt , c1 )
2011-09-10 11:13:51 +02:00
os . remove ( tgt )
except OSError :
pass
2013-09-29 23:45:36 +02:00
self . rm_empty_dirs ( tgt )
2011-09-10 11:13:51 +02:00
2016-04-28 00:23:54 +02:00
class InstallContext ( BuildContext ) :
''' installs the targets on the system '''
cmd = ' install '
def __init__ ( self , * * kw ) :
super ( InstallContext , self ) . __init__ ( * * kw )
self . is_install = INSTALL
class UninstallContext ( InstallContext ) :
''' removes the targets installed '''
cmd = ' uninstall '
def __init__ ( self , * * kw ) :
super ( UninstallContext , self ) . __init__ ( * * kw )
self . is_install = UNINSTALL
2011-09-10 11:13:51 +02:00
class CleanContext ( BuildContext ) :
''' cleans the project '''
cmd = ' clean '
def execute ( self ) :
"""
2016-06-24 13:21:32 +02:00
See : py : func : ` waflib . Build . BuildContext . execute ` .
2011-09-10 11:13:51 +02:00
"""
self . restore ( )
if not self . all_envs :
self . load_envs ( )
self . recurse ( [ self . run_dir ] )
try :
self . clean ( )
finally :
self . store ( )
def clean ( self ) :
2017-12-23 15:05:29 +01:00
"""
Remove most files from the build directory , and reset all caches .
Custom lists of files to clean can be declared as ` bld . clean_files ` .
For example , exclude ` build / program / myprogram ` from getting removed : :
def build ( bld ) :
bld . clean_files = bld . bldnode . ant_glob ( ' ** ' ,
excl = ' .lock* config.log c4che/* config.h program/myprogram ' ,
quiet = True , generator = True )
"""
2011-09-10 11:13:51 +02:00
Logs . debug ( ' build: clean called ' )
2017-12-23 15:05:29 +01:00
if hasattr ( self , ' clean_files ' ) :
for n in self . clean_files :
n . delete ( )
elif self . bldnode != self . srcnode :
2011-09-10 11:13:51 +02:00
# would lead to a disaster if top == out
2016-05-13 23:20:07 +02:00
lst = [ ]
for env in self . all_envs . values ( ) :
lst . extend ( self . root . find_or_declare ( f ) for f in env [ CFG_FILES ] )
2018-10-30 12:00:48 +01:00
excluded_dirs = ' .lock* *conf_check_*/** config.log %s /* ' % CACHE_DIR
for n in self . bldnode . ant_glob ( ' **/* ' , excl = excluded_dirs , quiet = True ) :
2011-09-10 11:13:51 +02:00
if n in lst :
continue
n . delete ( )
self . root . children = { }
2016-02-14 11:10:38 +01:00
for v in SAVED_ATTRS :
if v == ' root ' :
continue
2011-09-10 11:13:51 +02:00
setattr ( self , v , { } )
class ListContext ( BuildContext ) :
''' lists the targets to execute '''
cmd = ' list '
2016-06-24 13:21:32 +02:00
2011-09-10 11:13:51 +02:00
def execute ( self ) :
"""
2017-03-07 07:08:20 +01:00
In addition to printing the name of each build target ,
a description column will include text for each task
generator which has a " description " field set .
2016-06-24 13:21:32 +02:00
See : py : func : ` waflib . Build . BuildContext . execute ` .
2011-09-10 11:13:51 +02:00
"""
self . restore ( )
if not self . all_envs :
self . load_envs ( )
self . recurse ( [ self . run_dir ] )
self . pre_build ( )
# display the time elapsed in the progress bar
self . timer = Utils . Timer ( )
for g in self . groups :
for tg in g :
try :
f = tg . post
except AttributeError :
pass
else :
f ( )
try :
# force the cache initialization
self . get_tgen_by_name ( ' ' )
2016-05-13 23:20:07 +02:00
except Errors . WafError :
2011-09-10 11:13:51 +02:00
pass
2017-03-08 18:26:20 +01:00
2017-03-07 07:08:20 +01:00
targets = sorted ( self . task_gen_cache_names )
# figure out how much to left-justify, for largest target name
line_just = max ( len ( t ) for t in targets ) if targets else 0
for target in targets :
tgen = self . task_gen_cache_names [ target ]
# Support displaying the description for the target
# if it was set on the tgen
2017-03-08 18:26:20 +01:00
descript = getattr ( tgen , ' description ' , ' ' )
2017-03-07 07:08:20 +01:00
if descript :
target = target . ljust ( line_just )
2017-03-08 18:26:20 +01:00
descript = ' : %s ' % descript
2016-05-13 23:20:07 +02:00
2017-03-07 07:08:20 +01:00
Logs . pprint ( ' GREEN ' , target , label = descript )
2011-09-10 11:13:51 +02:00
class StepContext ( BuildContext ) :
''' executes tasks in a step-by-step fashion, for debugging '''
cmd = ' step '
def __init__ ( self , * * kw ) :
super ( StepContext , self ) . __init__ ( * * kw )
self . files = Options . options . files
def compile ( self ) :
"""
2016-06-24 13:21:32 +02:00
Overrides : py : meth : ` waflib . Build . BuildContext . compile ` to perform a partial build
on tasks matching the input / output pattern given ( regular expression matching ) : :
2011-09-10 11:13:51 +02:00
$ waf step - - files = foo . c , bar . c , in : truc . c , out : bar . o
$ waf step - - files = in : foo . cpp .1 . o # link task only
"""
if not self . files :
Logs . warn ( ' Add a pattern for the debug build, for example " waf step --files=main.c,app " ' )
BuildContext . compile ( self )
return
2016-05-13 23:20:07 +02:00
targets = [ ]
2012-03-25 10:33:57 +02:00
if self . targets and self . targets != ' * ' :
targets = self . targets . split ( ' , ' )
2011-09-10 11:13:51 +02:00
for g in self . groups :
for tg in g :
2012-03-25 10:33:57 +02:00
if targets and tg . name not in targets :
continue
2011-09-10 11:13:51 +02:00
try :
f = tg . post
except AttributeError :
pass
else :
f ( )
for pat in self . files . split ( ' , ' ) :
matcher = self . get_matcher ( pat )
for tg in g :
2017-02-11 15:04:25 +01:00
if isinstance ( tg , Task . Task ) :
2011-09-10 11:13:51 +02:00
lst = [ tg ]
else :
lst = tg . tasks
for tsk in lst :
do_exec = False
2017-02-19 11:05:44 +01:00
for node in tsk . inputs :
2011-09-10 11:13:51 +02:00
if matcher ( node , output = False ) :
do_exec = True
break
2017-02-19 11:05:44 +01:00
for node in tsk . outputs :
2011-09-10 11:13:51 +02:00
if matcher ( node , output = True ) :
do_exec = True
break
if do_exec :
ret = tsk . run ( )
2016-05-28 16:18:51 +02:00
Logs . info ( ' %s -> exit %r ' , tsk , ret )
2011-09-10 11:13:51 +02:00
def get_matcher ( self , pat ) :
2016-06-24 13:21:32 +02:00
"""
Converts a step pattern into a function
: param : pat : pattern of the form in : truc . c , out : bar . o
: returns : Python function that uses Node objects as inputs and returns matches
: rtype : function
"""
2011-09-10 11:13:51 +02:00
# this returns a function
inn = True
out = True
if pat . startswith ( ' in: ' ) :
out = False
pat = pat . replace ( ' in: ' , ' ' )
elif pat . startswith ( ' out: ' ) :
inn = False
pat = pat . replace ( ' out: ' , ' ' )
anode = self . root . find_node ( pat )
pattern = None
if not anode :
if not pat . startswith ( ' ^ ' ) :
pat = ' ^.+? %s ' % pat
if not pat . endswith ( ' $ ' ) :
pat = ' %s $ ' % pat
pattern = re . compile ( pat )
def match ( node , output ) :
2017-04-22 22:08:00 +02:00
if output and not out :
2011-09-10 11:13:51 +02:00
return False
2017-04-22 22:08:00 +02:00
if not output and not inn :
2011-09-10 11:13:51 +02:00
return False
if anode :
return anode == node
else :
return pattern . match ( node . abspath ( ) )
return match
2016-04-11 23:28:33 +02:00
class EnvContext ( BuildContext ) :
""" Subclass EnvContext to create commands that require configuration data in ' env ' """
fun = cmd = None
def execute ( self ) :
2016-06-24 13:21:32 +02:00
"""
See : py : func : ` waflib . Build . BuildContext . execute ` .
"""
2016-04-11 23:28:33 +02:00
self . restore ( )
if not self . all_envs :
self . load_envs ( )
self . recurse ( [ self . run_dir ] )