summaryrefslogtreecommitdiffstats
path: root/src/boost/tools/build/src/engine/rules.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/boost/tools/build/src/engine/rules.cpp')
-rw-r--r--src/boost/tools/build/src/engine/rules.cpp735
1 files changed, 735 insertions, 0 deletions
diff --git a/src/boost/tools/build/src/engine/rules.cpp b/src/boost/tools/build/src/engine/rules.cpp
new file mode 100644
index 000000000..a4e2ad107
--- /dev/null
+++ b/src/boost/tools/build/src/engine/rules.cpp
@@ -0,0 +1,735 @@
+/*
+ * Copyright 1993, 1995 Christopher Seiwald.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/* This file is ALSO:
+ * Copyright 2022 René Ferdinand Rivera Morell
+ * Copyright 2001-2004 David Abrahams.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+ */
+
+/*
+ * rules.c - access to RULEs, TARGETs, and ACTIONs
+ *
+ * External routines:
+ * bindrule() - return pointer to RULE, creating it if necessary.
+ * bindtarget() - return pointer to TARGET, creating it if necessary.
+ * touch_target() - mark a target to simulate being new.
+ * targetlist() - turn list of target names into a TARGET chain.
+ * targetentry() - add a TARGET to a chain of TARGETS.
+ * actionlist() - append to an ACTION chain.
+ * addsettings() - add a deferred "set" command to a target.
+ * pushsettings() - set all target specific variables.
+ * popsettings() - reset target specific variables to their pre-push values.
+ * freesettings() - delete a settings list.
+ * rules_done() - free RULE and TARGET tables.
+ */
+
+#include "jam.h"
+#include "rules.h"
+
+#include "hash.h"
+#include "lists.h"
+#include "object.h"
+#include "output.h"
+#include "parse.h"
+#include "pathsys.h"
+#include "search.h"
+#include "variable.h"
+
+
+static void set_rule_actions( rule_ptr, rule_actions_ptr );
+static void set_rule_body ( rule_ptr, function_ptr );
+
+static struct hash * targethash = 0;
+
+
+/*
+ * get_target_includes() - lazy creates a target's internal includes node
+ *
+ * The newly created node is not entered into the hash table as there should
+ * never be a need to bind them directly from a target names. If you want to
+ * access an internal includes node by name, first access the actual target and
+ * then read the internal includes node from there.
+ */
+
+static target_ptr get_target_includes( target_ptr const t )
+{
+ if ( !t->includes )
+ {
+ target_ptr const i = b2::jam::make_ptr<_target>();
+ i->name = object_copy( t->name );
+ i->boundname = object_copy( i->name );
+ i->flags |= T_FLAG_NOTFILE | T_FLAG_INTERNAL;
+ t->includes = i;
+ }
+ return t->includes;
+}
+
+
+/*
+ * target_include() - adds a target to the given targe's 'included' list
+ * target_include_many() - adds targets to the given target's 'included' list
+ *
+ * Included targets are modeled as dependencies of the including target's
+ * internal include node.
+ */
+
+void target_include( target_ptr const including, target_ptr const included )
+{
+ target_ptr const internal = get_target_includes( including );
+ targetentry( internal->depends, included );
+}
+
+void target_include_many( target_ptr const including, list_ptr const included_names
+ )
+{
+ target_ptr const internal = get_target_includes( including );
+ targetlist( internal->depends, included_names );
+}
+
+
+/*
+ * enter_rule() - return pointer to RULE, creating it if necessary in
+ * target_module.
+ */
+
+static rule_ptr enter_rule( object_ptr rulename, module_ptr target_module )
+{
+ int found;
+ rule_ptr const r = (rule_ptr)hash_insert( demand_rules( target_module ),
+ rulename, &found );
+ if ( !found )
+ {
+ r->name = object_copy( rulename );
+ r->procedure = 0;
+ r->module = 0;
+ r->actions = 0;
+ r->exported = 0;
+ r->module = target_module;
+ }
+ return r;
+}
+
+
+/*
+ * define_rule() - return pointer to RULE, creating it if necessary in
+ * target_module. Prepare it to accept a body or action originating in
+ * src_module.
+ */
+
+static rule_ptr define_rule( module_ptr src_module, object_ptr rulename,
+ module_ptr target_module )
+{
+ rule_ptr const r = enter_rule( rulename, target_module );
+ if ( r->module != src_module )
+ {
+ /* If the rule was imported from elsewhere, clear it now. */
+ set_rule_body( r, 0 );
+ set_rule_actions( r, 0 );
+ /* r will be executed in the source module. */
+ r->module = src_module;
+ }
+ return r;
+}
+
+
+void rule_free( rule_ptr r )
+{
+ object_free( r->name );
+ r->name = 0;
+ if ( r->procedure )
+ function_free( r->procedure );
+ r->procedure = 0;
+ if ( r->actions )
+ actions_free( r->actions );
+ r->actions = 0;
+}
+
+
+/*
+ * bindtarget() - return pointer to TARGET, creating it if necessary.
+ */
+
+target_ptr bindtarget( object_ptr const target_name )
+{
+ int found;
+ target_ptr t;
+
+ if ( !targethash )
+ targethash = hashinit( sizeof( TARGET ), "targets" );
+
+ t = (target_ptr)hash_insert( targethash, target_name, &found );
+ if ( !found )
+ {
+ b2::jam::ctor_ptr<_target>(t);
+ t->name = object_copy( target_name );
+ t->boundname = object_copy( t->name ); /* default for T_FLAG_NOTFILE */
+ }
+
+ return t;
+}
+
+
+static void bind_explicitly_located_target( target_ptr t, void * )
+{
+ if ( !( t->flags & T_FLAG_NOTFILE ) )
+ {
+ /* Check if there is a setting for LOCATE. */
+ settings_ptr s = t->settings;
+ for ( ; s ; s = s->next )
+ {
+ if ( object_equal( s->symbol, constant_LOCATE ) && ! list_empty( s->value ) )
+ {
+ set_explicit_binding( t->name, list_front( s->value ) );
+ break;
+ }
+ }
+ }
+}
+
+
+void bind_explicitly_located_targets()
+{
+ if ( targethash )
+ hash_enumerate( targethash, bind_explicitly_located_target );
+}
+
+
+/*
+ * touch_target() - mark a target to simulate being new.
+ */
+
+void touch_target( object_ptr const t )
+{
+ bindtarget( t )->flags |= T_FLAG_TOUCHED;
+}
+
+
+/*
+ * target_scc() - returns the root of a strongly connected component that this
+ * target is a part of.
+ */
+
+target_ptr target_scc( target_ptr t )
+{
+ target_ptr result = t;
+ while ( result->scc_root )
+ result = result->scc_root;
+ while ( t->scc_root )
+ {
+ target_ptr const tmp = t->scc_root;
+ t->scc_root = result;
+ t = tmp;
+ }
+ return result;
+}
+
+
+/*
+ * targetlist() - turn list of target names into a TARGET chain.
+ *
+ * Inputs:
+ * chain existing TARGETS to append to
+ * targets list of target names
+ */
+
+void targetlist( targets_uptr& chain, list_ptr target_names )
+{
+ LISTITER iter = list_begin( target_names );
+ LISTITER const end = list_end( target_names );
+ for ( ; iter != end; iter = list_next( iter ) )
+ targetentry( chain, bindtarget( list_item( iter ) ) );
+}
+
+
+/*
+ * targetentry() - add a TARGET to a chain of TARGETS.
+ *
+ * Inputs:
+ * chain existing TARGETS to append to
+ * target new target to append
+ */
+
+void targetentry( targets_uptr& chain, target_ptr target )
+{
+ auto c = b2::jam::make_unique_jptr<TARGETS>();
+ c->target = target;
+
+ targets_ptr tail = c.get();
+ if ( !chain ) chain.reset(c.release());
+ else chain->tail->next.reset(c.release());
+ chain->tail = tail;
+}
+
+
+/*
+ * targetchain() - append two TARGET chains.
+ *
+ * Inputs:
+ * chain existing TARGETS to append to
+ * target new target to append
+ */
+
+targets_uptr targetchain( targets_uptr chain, targets_uptr targets )
+{
+ if ( !targets ) return chain;
+ if ( !chain ) return targets;
+
+ targets_ptr tail = targets->tail;
+ chain->tail->next = std::move(targets);
+ chain->tail = tail;
+ return chain;
+}
+
+/*
+ * targets_pop() - removes the first TARGET from the chain.
+ */
+
+targets_uptr targets_pop(targets_uptr chain)
+{
+ targets_uptr result;
+ if ( chain && chain->next )
+ {
+ chain->next->tail = chain->tail;
+ result = std::move( chain->next );
+ }
+ return result;
+}
+
+/*
+ * action_free - decrement the ACTIONs reference count and (maybe) free it.
+ */
+
+void action_free( action_ptr action )
+{
+ if ( --action->refs == 0 )
+ {
+ b2::jam::free_ptr(action);
+ }
+}
+
+
+/*
+ * actionlist() - append to an ACTION chain.
+ */
+
+actions_ptr actionlist( actions_ptr chain, action_ptr action )
+{
+ actions_ptr const actions = (actions_ptr)BJAM_MALLOC( sizeof( ACTIONS ) );
+ actions->action = action;
+ ++action->refs;
+ if ( !chain ) chain = actions;
+ else chain->tail->next = actions;
+ chain->tail = actions;
+ actions->next = 0;
+ return chain;
+}
+
+static settings_ptr settings_freelist;
+
+
+/*
+ * addsettings() - add a deferred "set" command to a target.
+ *
+ * Adds a variable setting (varname=list) onto a chain of settings for a
+ * particular target. 'flag' controls the relationship between new and old
+ * values in the same way as in var_set() function (see variable.c). Returns the
+ * head of the settings chain.
+ */
+
+settings_ptr addsettings( settings_ptr head, int flag, object_ptr symbol,
+ list_ptr value )
+{
+ settings_ptr v;
+
+ /* Look for previous settings. */
+ for ( v = head; v; v = v->next )
+ if ( object_equal( v->symbol, symbol ) )
+ break;
+
+ /* If not previously set, alloc a new. */
+ /* If appending, do so. */
+ /* Else free old and set new. */
+ if ( !v )
+ {
+ v = settings_freelist;
+ if ( v )
+ settings_freelist = v->next;
+ else
+ v = (settings_ptr)BJAM_MALLOC( sizeof( *v ) );
+
+ v->symbol = object_copy( symbol );
+ v->value = value;
+ v->next = head;
+ head = v;
+ }
+ else if ( flag == VAR_APPEND )
+ {
+ v->value = list_append( v->value, value );
+ }
+ else if ( flag != VAR_DEFAULT )
+ {
+ list_free( v->value );
+ v->value = value;
+ }
+ else
+ list_free( value );
+
+ /* Return (new) head of list. */
+ return head;
+}
+
+
+/*
+ * pushsettings() - set all target specific variables.
+ */
+
+void pushsettings( module_ptr module, settings_ptr v )
+{
+ for ( ; v; v = v->next )
+ v->value = var_swap( module, v->symbol, v->value );
+}
+
+
+/*
+ * popsettings() - reset target specific variables to their pre-push values.
+ */
+
+void popsettings( module_ptr module, settings_ptr v )
+{
+ pushsettings( module, v ); /* just swap again */
+}
+
+
+/*
+ * copysettings() - duplicate a settings list, returning the new copy.
+ */
+
+settings_ptr copysettings( settings_ptr head )
+{
+ settings_ptr copy = 0;
+ settings_ptr v;
+ for ( v = head; v; v = v->next )
+ copy = addsettings( copy, VAR_SET, v->symbol, list_copy( v->value ) );
+ return copy;
+}
+
+
+/*
+ * freeactions() - delete an action list.
+ */
+
+void freeactions( actions_ptr chain )
+{
+ while ( chain )
+ {
+ actions_ptr const n = chain->next;
+ action_free( chain->action );
+ BJAM_FREE( chain );
+ chain = n;
+ }
+}
+
+
+/*
+ * freesettings() - delete a settings list.
+ */
+
+void freesettings( settings_ptr v )
+{
+ while ( v )
+ {
+ settings_ptr const n = v->next;
+ object_free( v->symbol );
+ list_free( v->value );
+ v->next = settings_freelist;
+ settings_freelist = v;
+ v = n;
+ }
+}
+
+
+static void freetarget( target_ptr const t, void * )
+{
+ if ( t->name ) object_free ( t->name );
+ if ( t->boundname ) object_free ( t->boundname );
+ if ( t->settings ) freesettings( t->settings );
+ if ( t->depends ) t->depends.reset();
+ if ( t->dependants ) t->dependants.reset();
+ if ( t->parents ) t->parents.reset();
+ if ( t->actions ) freeactions ( t->actions );
+ if ( t->includes )
+ {
+ freetarget( t->includes, (void *)0 );
+ BJAM_FREE( t->includes );
+ }
+ t->~_target();
+}
+
+
+/*
+ * rules_done() - free RULE and TARGET tables.
+ */
+
+void rules_done()
+{
+ if ( targethash )
+ {
+ hash_enumerate( targethash, freetarget );
+ hashdone( targethash );
+ }
+ while ( settings_freelist )
+ {
+ settings_ptr const n = settings_freelist->next;
+ BJAM_FREE( settings_freelist );
+ settings_freelist = n;
+ }
+}
+
+
+/*
+ * actions_refer() - add a new reference to the given actions.
+ */
+
+void actions_refer( rule_actions_ptr a )
+{
+ ++a->reference_count;
+}
+
+
+/*
+ * actions_free() - release a reference to given actions.
+ */
+
+void actions_free( rule_actions_ptr a )
+{
+ if ( --a->reference_count <= 0 )
+ {
+ function_free( a->command );
+ list_free( a->bindlist );
+ BJAM_FREE( a );
+ }
+}
+
+
+/*
+ * set_rule_body() - set the argument list and procedure of the given rule.
+ */
+
+static void set_rule_body( rule_ptr rule, function_ptr procedure )
+{
+ if ( procedure )
+ function_refer( procedure );
+ if ( rule->procedure )
+ function_free( rule->procedure );
+ rule->procedure = procedure;
+}
+
+
+/*
+ * global_name() - given a rule, return the name for a corresponding rule in the
+ * global module.
+ */
+
+static object_ptr global_rule_name( rule_ptr r )
+{
+ if ( r->module == root_module() )
+ return object_copy( r->name );
+
+ {
+ char name[ 4096 ] = "";
+ if ( r->module->name )
+ {
+ strncat( name, object_str( r->module->name ), sizeof( name ) - 1 );
+ strncat( name, ".", sizeof( name ) - 1 );
+ }
+ strncat( name, object_str( r->name ), sizeof( name ) - 1 );
+ return object_new( name );
+ }
+}
+
+
+/*
+ * global_rule() - given a rule, produce a corresponding entry in the global
+ * module.
+ */
+
+static rule_ptr global_rule( rule_ptr r )
+{
+ if ( r->module == root_module() )
+ return r;
+
+ {
+ object_ptr const name = global_rule_name( r );
+ rule_ptr const result = define_rule( r->module, name, root_module() );
+ object_free( name );
+ return result;
+ }
+}
+
+
+/*
+ * new_rule_body() - make a new rule named rulename in the given module, with
+ * the given argument list and procedure. If exported is true, the rule is
+ * exported to the global module as modulename.rulename.
+ */
+
+rule_ptr new_rule_body( module_ptr m, object_ptr rulename, function_ptr procedure,
+ int exported )
+{
+ rule_ptr const local = define_rule( m, rulename, m );
+ local->exported = exported;
+ set_rule_body( local, procedure );
+
+ /* Mark the procedure with the global rule name, regardless of whether the
+ * rule is exported. That gives us something reasonably identifiable that we
+ * can use, e.g. in profiling output. Only do this once, since this could be
+ * called multiple times with the same procedure.
+ */
+ if ( !function_rulename( procedure ) )
+ function_set_rulename( procedure, global_rule_name( local ) );
+
+ return local;
+}
+
+
+static void set_rule_actions( rule_ptr rule, rule_actions_ptr actions )
+{
+ if ( actions )
+ actions_refer( actions );
+ if ( rule->actions )
+ actions_free( rule->actions );
+ rule->actions = actions;
+}
+
+
+static rule_actions_ptr actions_new( function_ptr command, list_ptr bindlist,
+ int flags )
+{
+ rule_actions_ptr const result = (rule_actions_ptr)BJAM_MALLOC( sizeof(
+ rule_actions ) );
+ function_refer( command );
+ result->command = command;
+ result->bindlist = bindlist;
+ result->flags = flags;
+ result->reference_count = 0;
+ return result;
+}
+
+
+rule_ptr new_rule_actions( module_ptr m, object_ptr rulename, function_ptr command,
+ list_ptr bindlist, int flags )
+{
+ rule_ptr const local = define_rule( m, rulename, m );
+ rule_ptr const global = global_rule( local );
+ set_rule_actions( local, actions_new( command, bindlist, flags ) );
+ set_rule_actions( global, local->actions );
+ return local;
+}
+
+
+/*
+ * Looks for a rule in the specified module, and returns it, if found. First
+ * checks if the rule is present in the module's rule table. Second, if the
+ * rule's name is in the form name1.name2 and name1 is in the list of imported
+ * modules, look in module 'name1' for rule 'name2'.
+ */
+
+rule_ptr lookup_rule( object_ptr rulename, module_ptr m, int local_only )
+{
+ rule_ptr r;
+ rule_ptr result = 0;
+ module_ptr original_module = m;
+
+ if ( m->class_module )
+ m = m->class_module;
+
+ if ( m->rules && ( r = (rule_ptr)hash_find( m->rules, rulename ) ) )
+ result = r;
+ else if ( !local_only && m->imported_modules )
+ {
+ /* Try splitting the name into module and rule. */
+ const char * p = strchr( object_str( rulename ), '.' ) ;
+ if ( p )
+ {
+ /* Now, r->name keeps the module name, and p + 1 keeps the rule
+ * name.
+ */
+ object_ptr rule_part = object_new( p + 1 );
+ object_ptr module_part;
+ {
+ string buf[ 1 ];
+ string_new( buf );
+ string_append_range( buf, object_str( rulename ), p );
+ module_part = object_new( buf->value );
+ string_free( buf );
+ }
+ if ( hash_find( m->imported_modules, module_part ) )
+ result = lookup_rule( rule_part, bindmodule( module_part ), 1 );
+ object_free( module_part );
+ object_free( rule_part );
+ }
+ }
+
+ if ( result )
+ {
+ if ( local_only && !result->exported )
+ result = 0;
+ else if ( original_module != m )
+ {
+ /* Lookup started in class module. We have found a rule in class
+ * module, which is marked for execution in that module, or in some
+ * instance. Mark it for execution in the instance where we started
+ * the lookup.
+ */
+ int const execute_in_class = result->module == m;
+ int const execute_in_some_instance =
+ result->module->class_module == m;
+ if ( execute_in_class || execute_in_some_instance )
+ result->module = original_module;
+ }
+ }
+
+ return result;
+}
+
+
+rule_ptr bindrule( object_ptr rulename, module_ptr m )
+{
+ rule_ptr result = lookup_rule( rulename, m, 0 );
+ if ( !result )
+ result = lookup_rule( rulename, root_module(), 0 );
+ /* We have only one caller, 'evaluate_rule', which will complain about
+ * calling an undefined rule. We could issue the error here, but we do not
+ * have the necessary information, such as frame.
+ */
+ if ( !result )
+ result = enter_rule( rulename, m );
+ return result;
+}
+
+
+rule_ptr import_rule( rule_ptr source, module_ptr m, object_ptr name )
+{
+ rule_ptr const dest = define_rule( source->module, name, m );
+ set_rule_body( dest, source->procedure );
+ set_rule_actions( dest, source->actions );
+ return dest;
+}
+
+
+void rule_localize( rule_ptr rule, module_ptr m )
+{
+ rule->module = m;
+ if ( rule->procedure )
+ {
+ function_ptr procedure = function_unbind_variables( rule->procedure );
+ function_refer( procedure );
+ function_free( rule->procedure );
+ rule->procedure = procedure;
+ }
+}