// file : libbuild2/file.hxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #ifndef LIBBUILD2_FILE_HXX #define LIBBUILD2_FILE_HXX #include #include #include #include #include #include // list_value #include namespace build2 { class lexer; class parser; // The following filesystem entries in the build/ subdirectory are reserved // by the build2 core: // // build/ -- build2 core-internal build state (e.g., recipes) // bootstrap/ -- bootstrap state and hooks // bootstrap.build -- bootstrap buildfile // root/ -- root load hooks // root.build -- root buildfile // export.build -- export stub // export/ -- exported buildfiles // // The build/, bootstrap/, root/, and config.build entries are in .gitignore // as generated by bdep-new. // // The rest of the filesystem entries are shared between the project and the // modules that it loads. In particular, if a project loads module named // , then the .build, /, *. entries (spelled in any // case) are reserved to this module and should not be used by the project // unless explicitly allowed by the module. By convention, /build/ is // for module-internal build state (e.g., C++ modules side-build) and is // .gitignore'ed. // LIBBUILD2_SYMEXPORT extern const dir_path std_build_dir; // build/ // build/root.build // LIBBUILD2_SYMEXPORT extern const path std_root_file; // build/bootstrap.build // LIBBUILD2_SYMEXPORT extern const path std_bootstrap_file; LIBBUILD2_SYMEXPORT extern const path std_buildfile_file; // buildfile LIBBUILD2_SYMEXPORT extern const path alt_buildfile_file; // build2file // If the altn argument value is present, then it indicates whether we are // using the standard or the alternative build file/directory naming. // // The overall plan is to run various "file exists" tests using the standard // and the alternative names. The first test that succeeds determines the // naming scheme (by setting altn) and from then on all the remaining tests // only look for things in this scheme. // LIBBUILD2_SYMEXPORT bool is_src_root (const dir_path&, optional& altn); LIBBUILD2_SYMEXPORT bool is_out_root (const dir_path&, optional& altn); // Given an src_base directory, look for a project's src_root based on the // presence of known special files. Return empty path if not found. Note // that if the input is normalized/actualized, then the output will be as // well. // LIBBUILD2_SYMEXPORT dir_path find_src_root (const dir_path&, optional& altn); // The same as above but for project's out. Note that we also check whether // a directory happens to be src_root, in case this is an in source build with // the result returned as the second half of the pair. Note also that if the // input is normalized/actualized, then the output will be as well. // LIBBUILD2_SYMEXPORT pair find_out_root (const dir_path&, optional& altn); // Look for a buildfile starting from the specified directory and continuing // in the parent directories until root. Return nullopt if not found. // LIBBUILD2_SYMEXPORT optional find_buildfile (const dir_path& base, const dir_path& root, optional& altn, const path& name = {}); // If the buildfile cannot be found in the target's directory itself, then // this function can be used to try and find a nearest buildfile that could // plausibly define this target. Return nullopt if not found and empty path // if the implied buildfile in the target's directory should be assumed. // LIBBUILD2_SYMEXPORT optional find_plausible_buildfile (const name& tgt, const scope& rs, const dir_path& src_base, const dir_path& src_root, optional& altn, const path& name = {}); // Project's loading stage during which the parsing is performed. // enum class load_stage { boot, // Loading bootstrap.build (or bootstrap pre/post hooks). root, // Loading root.build (or root pre/post hooks). rest // Loading the rest (ordinary buildfiles, command line, etc). }; // If buildfile is '-', then read from STDIN. // LIBBUILD2_SYMEXPORT void source (scope& root, scope& base, const path&); // As above, but extract from a stream. The name argument is used for // diagnostics. // LIBBUILD2_SYMEXPORT void source (scope& root, scope& base, istream&, const path_name&); // As above, but extract from a lexer (this could be useful for sourcing // stdin that requires parse_variable()). // LIBBUILD2_SYMEXPORT void source (scope& root, scope& base, lexer&, load_stage = load_stage::rest); // As above but first check if this buildfile has already been sourced for // the root scope. Return false if the file has already been sourced. // bool source_once (scope& root, scope& base, const path&); // As above but checks against the specified root scope rather than this // root scope. // LIBBUILD2_SYMEXPORT bool source_once (scope& root, scope& base, const path&, scope& once_root); // Create project's root scope. Only set the src_root variable if the passed // src_root value is not empty. // LIBBUILD2_SYMEXPORT scope_map::iterator create_root (context&, const dir_path& out_root, const dir_path& src_root); // Setup root scope. Note that it assumes the src_root variable has already // been set. // LIBBUILD2_SYMEXPORT void setup_root (scope&, bool forwarded); // Setup the base scope (set *_base variables, etc). // LIBBUILD2_SYMEXPORT scope& setup_base (scope_map::iterator, const dir_path& out_base, const dir_path& src_base); // Return a scope for the specified directory (first). If project is true // then switching to this scope might also involve switch to a new root // scope (second) if the new scope is in another project. If project is // false or the new scope is not in any project, then NULL is returned in // second. // // Note that if switching the scope involved switching the project, then you // may also need to switch to the new project's environment (see // root_extra::environment). // LIBBUILD2_SYMEXPORT pair switch_scope (scope& root, const dir_path& out_base, bool project = true); // Bootstrap and optionally load an ad hoc (sub)project (i.e., the kind that // is not discovered and loaded automatically by bootstrap/load functions // above). // // Note that we expect the outer project (if any) to be bootstrapped and // loaded and currently we do not add the newly loaded subproject to the // outer project's subprojects map. // LIBBUILD2_SYMEXPORT scope& load_project (context&, const dir_path& out_root, const dir_path& src_root, bool forwarded, bool load = true); // Bootstrap the project's forward. Return the forwarded-to out_root or // src_root if there is no forward. See is_{src,out}_root() for the altn // argument semantics. // LIBBUILD2_SYMEXPORT dir_path bootstrap_fwd (context&, const dir_path& src_root, optional& altn); // Bootstrap the project's root scope, the out part. Return the src_root // variable value (which can be NULL). // LIBBUILD2_SYMEXPORT value& bootstrap_out (scope& root, optional& altn); // Bootstrap the project's root scope, the src part. // // If amalgamation is present, then use the specified directory as the // amalgamation instead of discovering or extracting it from bootstrap.build // (use empty directory to disable amalgamation). If subprojects is false, // then do not discover or extract subprojects. // LIBBUILD2_SYMEXPORT void bootstrap_src (scope& root, optional& altn, optional amalgamation = nullopt, bool subprojects = true); // Return true if this scope has already been bootstrapped, that is, the // following calls have already been made: // // bootstrap_out() // setup_root() // bootstrap_src() // LIBBUILD2_SYMEXPORT bool bootstrapped (scope& root); // Execute pre/post-bootstrap hooks. Similar to bootstrap_out/src(), should // only be called once per project bootstrap. // LIBBUILD2_SYMEXPORT void bootstrap_pre (scope& root, optional& altn); LIBBUILD2_SYMEXPORT void bootstrap_post (scope& root); // Create and bootstrap outer root scopes, if any. Loading is done by // load_root(). If subprojects is false, then do not discover or extract // subprojects. // LIBBUILD2_SYMEXPORT void create_bootstrap_outer (scope& root, bool subprojects = true); // Create and bootstrap inner root scopes, if any, recursively. // // If out_base is not empty, then only bootstrap scope between root and base // returning the innermost created root scope or root if none were created. // // Note that loading is done by load_root(). // LIBBUILD2_SYMEXPORT scope& create_bootstrap_inner (scope& root, const dir_path& out_base = dir_path ()); // Load project's root.build (and root pre/post hooks) unless already // loaded. Also make sure all outer root scopes are loaded prior to loading // this root scope. // // If pre/post functions are specified, they are called before/after // pre/post hooks, respectively. // LIBBUILD2_SYMEXPORT void load_root (scope& root, const function& pre = nullptr, const function& post = nullptr); // Extract the specified variable value from a buildfile. It is expected to // be the first non-blank/comment line and not to rely on any variable // expansions other than those from the global scope or any variable // overrides. Return nullopt if the variable was not found. // LIBBUILD2_SYMEXPORT optional extract_variable (context&, const path&, const variable&); // As above, but extract from a stream. The path argument is used for // diagnostics. // LIBBUILD2_SYMEXPORT optional extract_variable (context&, istream&, const path&, const variable&); // As above, but extract from a lexer (this could be useful for extracting // from stdin). // LIBBUILD2_SYMEXPORT optional extract_variable (context&, lexer&, const variable&); // Import has two phases: the first is triggered by the import directive in // the buildfile. It will try to find and load the project. Failed that, it // will return the project-qualified name of the target which will be used // to create a project-qualified prerequisite. This gives the rule that will // be searching this prerequisite a chance to do some target-type specific // search. For example, a C++ link rule can search for lib{} prerequisites // in the C++ compiler default library search paths (so that we end up with // functionality identical to -lfoo). If, however, the rule didn't do any of // that (or failed to find anything usable), it calls the standard // prerequisite search() function which sees this is a project-qualified // prerequisite and goes straight to the second phase of import. Here, // currently, we only have special handling of exe{} targets (search in // PATH) simply failing for the rest. But in the future this coud be the // place where we could call custom "last resort" import hooks. For example, // we can hook a package manager that will say, "Hey, dude, I see you are // trying to import foo and I see there is a package foo available in // repository bar. Wanna, like, download and use it or something?" Though // the latest thoughts indicate this is probably a bad idea (implicitness, // complexity, etc). // // More specifically, we have the following kinds of import (tried in this // order): // // ad hoc // // The target is imported by having its path specifed directly with // config.import..[.]. For example, this can be used to // import an installed target. // // Note that this is also the kind assigned to an import that uses an // unqualified absolute target name or a relative directory (which we also // call ad hoc; in a sense it's the same thing, just the path is hardcoded // directly in the buildfile). // // // normal // // The target is imported from a project that was either specified with // config.import. or was found via the subproject search. This also // loads the target's dependency information. // // // rule-specific // // The target was imported in a rule-specific manner (e.g., a library was // found in the compiler's search paths). // // // fallback/default // // The target was found by the second phase of import (e.g., an executable // was found in PATH). // Import phase 1. Return the imported target(s) as well as the kind of // import that was performed with `fallback` indicating it was not found. // // If second is `fallback`, then first contains the original, project- // qualified target. If second is `adhoc`, first may still contain a // project-qualified target (which may or may not be the same as the // original; see the config.import..[.] logic for details) // in which case it should still be passed to import phase 2. // // If phase2 is present then the phase 2 is performed right away (we call it // immediate import). Note that if optional is true, phase2 must be present // as well (and thus there is no rule-specific logic for optional imports). // In case of optional, empty names value is returned if nothing was found. // The value in phase2 is the optional rule hint that, if not empty, will be // used to lookup a rule that will be asked to resolve the qualified target // (see rule::import()). If it is empty, then built-in resolution logic will // be used for some target types (currently only exe{}). // // If metadata is true, then load the target metadata. In this case phase2 // must be true as well. // // Note also that we return names rather than a single name: while normally // it will be a single target name, it can be an out-qualified pair (if // someone wants to return a source target) but it can also be a non-target // since we don't restrict what users can import/export. If name has // buildfile type, then the result is an absolute buildfile target to be // included (once) at the point of importation. // // Finally, note that import is (and should be kept) idempotent or, more // precisely, "accumulatively idempotent" in that additional steps may be // performed (phase 2, loading of the metadata) unless already done. // enum class import_kind {adhoc, normal, fallback}; template struct import_result { const T* target; // Note: T can be imported target or imported scope. names name; import_kind kind; }; // Note that import_result::target may be NULL even if name is not // empty (e.g, out of project target imported via phase 2). // LIBBUILD2_SYMEXPORT import_result import (scope& base, name, const optional& phase2, bool optional, bool metadata, const location&); // Import phase 2. // const target& import2 (context&, const prerequisite_key&); // As above but import the target "here and now" without waiting for phase 2 // (and thus omitting any rule-specific logic). This version of import is, // for example, used by build system modules to perform an implicit import // of the corresponding tool. // // If phase2 is absent, then the second phase's fallback/default logic is // only invoked if the import was ad hoc (i.e., a relative path was // specified via config.import..[.]) with NULL returned // otherwise. // // If phase2 is present and optional is true, then NULL is returned instead // of failing if phase 2 could not find anything. // // If metadata is true, then load the target metadata. In this case phase2 // must be present as well. // // The what argument specifies what triggered the import (for example, // "module load") and is used in diagnostics. // // This function also returns the stable exported target name (see // target::as_name() for details) as well as the kind of import that was // performed. // // Note: cannot be used to import buildfile targets (use import_buildfile() // instead). // Print import_direct() result either as a target for a normal import // or as a process path for ad hoc and fallback imports. Normally used in // build system modules to print the configuration report. // LIBBUILD2_SYMEXPORT ostream& operator<< (ostream&, const import_result&); import_result import_direct (scope& base, name, const optional& phase2, bool optional, bool metadata, const location&, const char* what = "import"); // As above but also return (in new_value) an indication of whether this // import is based on a new config.* value. See config::lookup_config() for // details. Note that a phase 2 fallback/default logic is not considered new // (though this can be easily adjusted based on import kind). // LIBBUILD2_SYMEXPORT import_result import_direct (bool& new_value, scope& base, name, const optional& phase2, bool optional, bool metadata, const location&, const char* what = "import"); // As above but also cast the target and pass phase2 as bool (primarily // for use in build system modules). // template import_result import_direct (scope&, name, bool, bool, bool, const location&, const char* = "import"); template import_result import_direct (bool&, scope&, name, bool, bool, bool, const location&, const char* = "import"); // The import_direct() equivalent for importing buildfile targets. Return // empty name if optional and not found. Note that the returned file path is // not necessarily checked for existence so sourcing it may still fail. // // Note also that this function can be used for an ad hoc import by passing // an absolute target name as would be returned by the normal import (can be // useful for importing own buildfiles). // LIBBUILD2_SYMEXPORT path import_buildfile (scope& base, name, bool optional, const location&); // As import phase 2 but only imports as an already existing target. But // unlike it, this function can be called during the load and execute // phases. // // Note: similar to search_existing(). // const target* import_existing (context&, const prerequisite_key&); // Lower-level components of phase 1 (see implementation for details). // pair> import_search (scope& base, name, bool optional_, const optional& metadata, // False or metadata key. bool subprojects, const location&, const char* what = "import"); // As above but also return (in new_value) an indication of whether this // import is based on a new config.* value. See config::lookup_config() // for details. // LIBBUILD2_SYMEXPORT pair> import_search (bool& new_value, scope& base, name, bool optional_, const optional& metadata, bool subprojects, const location&, const char* what = "import"); LIBBUILD2_SYMEXPORT pair import_load (context&, pair>, bool metadata, const location&); // Import (more precisely, alias as if using the `define =` syntax) the // target type from imported project (iroot) into this project (root). If // the target type with this name is already defined in this project, then // make sure it is the same as in the imported project. // LIBBUILD2_SYMEXPORT const target_type& import_target_type (scope& root, const scope& iroot, const string&, const location&); // Suggest appropriate ways to import the specified target (as type and // name) from the specified project. // void import_suggest (const diag_record&, const project_name&, const target_type*, const string& name, bool rule_hint, const char* qual = nullptr); // Create a build system project in the specified directory. // LIBBUILD2_SYMEXPORT void create_project ( const dir_path&, const optional& amalgamation, const strings& boot_modules, // Bootstrap modules. const string& root_pre, // Extra root.build text. const strings& root_modules, // Root modules. const string& root_post, // Extra root.build text. const optional& config_module, // Config module to load. const optional& config_file, // Ad hoc config.build contents. bool buildfile, // Create root buildfile. const char* who, // Who is creating it. uint16_t verbosity); // Diagnostic verbosity. } #include #endif // LIBBUILD2_FILE_HXX