Mercurial > nand-ecoscentric
diff host/libcdl/build.cxx @ 76:435cced73e2f ecos-v1_3_1-release
eCos v1.3.1 merged from eCos master repository on 2000-03-27-23:22:51-BST
| author | jlarmour |
|---|---|
| date | Tue, 28 Mar 2000 14:10:45 +0000 |
| parents | |
| children | 6736c52df507 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/host/libcdl/build.cxx @@ -0,0 +1,2091 @@ +//{{{ Banner + +//========================================================================== +// +// build.cxx +// +// libcdl support for building and for header file generation +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ---------------------------------------------------------------------------- +// Copyright (C) 1999, 2000 Red Hat, Inc. +// +// This file is part of the eCos host tools. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// ---------------------------------------------------------------------------- +// +//####COPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): bartv +// Contributors: bartv +// Date: 1999-06-018 +// +//####DESCRIPTIONEND#### +//========================================================================== + +//}}} +//{{{ #include's + +// ---------------------------------------------------------------------------- +#include "cdlconfig.h" + +// Get the infrastructure types, assertions, tracing and similar +// facilities. +#include <cyg/infra/cyg_ass.h> +#include <cyg/infra/cyg_trac.h> + +// <cdl.hxx> defines everything implemented in this module. +// It implicitly supplies <string>, <vector> and <map> because +// the class definitions rely on these headers. +#include <cdlcore.hxx> + +// split_custom_build_step() needs access to isspace() etc. +#include <cctype> + +//}}} + +//{{{ Statics + +// ---------------------------------------------------------------------------- +CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlBuildLoadableBody); +CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlBuildableBody); +CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlDefineLoadableBody); +CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlDefinableBody); + +//}}} +//{{{ CdlBuildableBody + +//{{{ Basics + +// ---------------------------------------------------------------------------- +// There is little data specific to a buildable. The only distinguishing +// feature is the set of properties that are supported, plus a handful +// of functions to extract that information. + +CdlBuildableBody::CdlBuildableBody() +{ + CYG_REPORT_FUNCNAME("CdlBuildable:: default constructor"); + CYG_REPORT_FUNCARG1XV(this); + + // There is no data to initialize yet + cdlbuildablebody_cookie = CdlBuildableBody_Magic; + CYGDBG_MEMLEAK_CONSTRUCTOR(); + + CYG_POSTCONDITION_THISC(); + CYG_REPORT_RETURN(); +} + +CdlBuildableBody::~CdlBuildableBody() +{ + CYG_REPORT_FUNCNAME("CdlBuildable:: destructor"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + cdlbuildablebody_cookie = CdlBuildableBody_Invalid; + CYGDBG_MEMLEAK_DESTRUCTOR(); + + CYG_REPORT_RETURN(); +} + +// ---------------------------------------------------------------------------- + +std::string +CdlBuildableBody::get_class_name() const +{ + CYG_REPORT_FUNCNAME("CdlBuildable::get_class_name"); + CYG_PRECONDITION_THISC(); + CYG_REPORT_RETURN(); + return "buildable"; +} + +// ---------------------------------------------------------------------------- + +bool +CdlBuildableBody::check_this(cyg_assert_class_zeal zeal) const +{ + if (CdlBuildableBody_Magic != cdlbuildablebody_cookie) { + return false; + } + CYGDBG_MEMLEAK_CHECKTHIS(); + return CdlNodeBody::check_this(zeal); +} + +//}}} +//{{{ Add and check property parsers + +// ---------------------------------------------------------------------------- + +void +CdlBuildableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers) +{ + CYG_REPORT_FUNCNAME("CdlBuildable::add_property_parsers"); + + static CdlInterpreterCommandEntry commands[] = + { + CdlInterpreterCommandEntry("compile", &CdlBuildableBody::parse_compile ), + CdlInterpreterCommandEntry("object", &CdlBuildableBody::parse_object ), + CdlInterpreterCommandEntry("make_object", &CdlBuildableBody::parse_make_object), + CdlInterpreterCommandEntry("make", &CdlBuildableBody::parse_make ), + CdlInterpreterCommandEntry("build_proc", &CdlBuildableBody::parse_build_proc ), + CdlInterpreterCommandEntry("", 0 ), + }; + + for (int i = 0; commands[i].command != 0; i++) { + std::vector<CdlInterpreterCommandEntry>::const_iterator j; + for (j = parsers.begin(); j != parsers.end(); j++) { + if (commands[i].name == j->name) { + if (commands[i].command != j->command) { + CYG_FAIL("Property names are being re-used"); + } + break; + } + } + if (j == parsers.end()) { + parsers.push_back(commands[i]); + } + } + CdlNodeBody::add_property_parsers(parsers); + + CYG_REPORT_RETURN(); +} + +void +CdlBuildableBody::check_properties(CdlInterpreter interp) +{ + CYG_REPORT_FUNCNAME("CdlBuildable::check_properties"); + CYG_REPORT_FUNCARG2XV(this, interp); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(interp); + + // There are no real constraints on the number of compile + // properties etc. + // TODO: check that the relevant sources files exist, + // unless marked appropriately (build_proc can create + // new source files). + + CdlNodeBody::check_properties(interp); + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ Property parsers + +// ---------------------------------------------------------------------------- +// Syntax: compile <file1 file2 ...> +// +// There are a couple of checks that could be performed here: +// +// 1) does each listed file actually exist? Unfortunately that approach +// falls foul of build_proc, which is allowed to generate source files +// on the fly. +// +// 2) does the file have a recognised suffix such as .c or .cxx. This +// relies libcdl having some way of knowing how to treat different +// files. +// +// For now there are no validity checks. +// +// A future extension may allow dependencies to be listed, as an +// option. This would allow component vendors to specify that +// particular custom build steps should happen before particular +// compilations, a more robust approach than the current priority +// scheme. + +int +CdlBuildableBody::parse_compile(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_compile", "result %d"); + static char* options[] = { + "library:", + 0 + }; + + int result = CdlParse::parse_stringvector_property(interp, argc, argv, CdlPropertyId_Compile, options, 0); + + CYG_REPORT_RETVAL(result); + return result; +} + +// ---------------------------------------------------------------------------- +// A utility to break a custom build step down into its three components. +// +// A custom build step takes the form: +// target : deps +// rules +// +// This utility function takes a single string of this form and breaks +// it down into its constituent parts. +// +// NOTE: this will need lots of extra code in future to allow for +// escaped characters, spaces in filenames, etc. For now just keep +// things simple. + +bool +CdlBuildableBody::split_custom_build_step(std::string str_data, std::string& target, std::string& deps, std::string& rules, + std::string& error_msg) +{ + CYG_REPORT_FUNCNAMETYPE("CdlBuildable::split_custom_build_step", "result %d"); + + target = ""; + deps = ""; + rules = ""; + error_msg = ""; + + const char* data = str_data.c_str(); + + // Skip any leading white space, and make sure that this leaves some real data. + while (('\0' != *data) && isspace(*data)) { + data++; + } + if ('\0' == *data) { + error_msg = "no data in custom build_step"; + CYG_REPORT_RETVAL(false); + return false; + } + + // Now extract the target. This consists of any sequence of characters + // upto space, tab, colon. + for ( ; ('\0' != *data) && (':' != *data) && (' ' != *data) && ('\t' != *data); data++) { + target += *data; + } + // Discard any spaces or tabs, they are of no interest + while ((' ' == *data) || ('\t' == *data)) { + data++; + } + // The current character should be a colon + if (':' != *data) { + error_msg = "expecting a colon `;' after the target `" + target + "'"; + CYG_REPORT_RETVAL(false); + return false; + } + + // Move past the colon, and skip any further spaces or tabs + data++; + while (('\0' != *data) && ((' ' == *data) || ('\t' == *data))) { + data++; + } + + // Everything from here until the end of line should be part of the deps field, + // including white space. + while (('\0' != *data) && ('\n' != *data) && (';' != *data)) { + deps += *data++; + } + + if ("" == deps) { + error_msg = "expecting dependency list after `" + target + ":'"; + CYG_REPORT_RETVAL(false); + return false; + } + + // Having some rules is compulsory. + if ('\0' == *data) { + error_msg = "expecting one or more rules after the dependency list"; + CYG_REPORT_RETVAL(false); + return false; + } else { + // We are currently at \n or ;, move on to the actual rules + data++; + } + + // Rules consist of one or more lines. Any leading white space on a given + // line should be discarded. + while ('\0' != *data) { + // Processing the current rule. Skip leading spaces and tabs + while ((' ' == *data) || ('\t' == *data)) { + data++; + } + // Now add everything up to the next newline or EOD to the rules. + while (('\0' != *data) && ('\n' != *data)) { + rules += *data++; + } + + // Terminate this line of the rules with a newline, even if that + // character is absent from the raw data. + rules += '\n'; + + // And ignore the newline in the raw data itself + if ('\n' == *data) { + data++; + } + } + + // Better make sure that there are some rules. All of the looping above + // may just have left white space + if ("" == rules) { + error_msg = "no rules provided"; + CYG_REPORT_RETVAL(false); + return false; + } + + // Everything is ok. + + CYG_REPORT_RETVAL(true); + return true; +} + + +// ---------------------------------------------------------------------------- +// syntax: make <target> <rules> +// +// There is rather a lot of checking to be done. +// +// 1) the priority should be valid. In particular it should be a number +// within a reasonable range. +// +// 2) the rules should take the form: +// <target> : <deps> ;|\n rules +// +// Where the target should be a single file, identical to the +// first property argument. + +static void +parse_make_final_check(CdlInterpreter interp, CdlProperty_String prop) +{ + CYG_REPORT_FUNCNAME("parse_make_final_check"); + CYG_PRECONDITION_CLASSC(interp); + CYG_PRECONDITION_CLASSC(prop); + + std::string prio_string = prop->get_option("priority"); + if ("" != prio_string) { + cdl_int tmp = 1; + if (!Cdl::string_to_integer(prio_string, tmp)) { + CdlParse::report_property_parse_error(interp, prop, + "Invalid priority option, priorities should be simple numbers."); + } else { + if ((tmp < 1) || (tmp > 1024)) { + CdlParse::report_property_parse_error(interp, prop, + "Invalid priority value, priorities should be in the range 1 to 1024."); + } + } + } + + std::string data = prop->get_string(); + std::string target; + std::string deps; + std::string rules; + std::string error_msg; + + if (!CdlBuildableBody::split_custom_build_step(data, target, deps, rules, error_msg)) { + CdlParse::report_property_parse_error(interp, prop, "Invalid custom build step, " + error_msg); + } +} + +int +CdlBuildableBody::parse_make(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_make", "result %d"); + static char* options[] = { + "priority:", + 0 + }; + + int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Make, options, &parse_make_final_check); + + CYG_REPORT_RETVAL(result); + return result; +} + + +// ---------------------------------------------------------------------------- +// syntax: make_object <target> <rules> +// +// The rules here are much the same as for the "make" property. + +static void +parse_make_object_final_check(CdlInterpreter interp, CdlProperty_String prop) +{ + CYG_REPORT_FUNCNAME("parse_make_object_final_check"); + CYG_PRECONDITION_CLASSC(interp); + CYG_PRECONDITION_CLASSC(prop); + + + std::string prio_string = prop->get_option("priority"); + if ("" != prio_string) { + cdl_int tmp = 1; + if (!Cdl::string_to_integer(prio_string, tmp)) { + CdlParse::report_property_parse_error(interp, prop, + "Invalid priority option, priorities should be simple numbers."); + } else { + if ((tmp < 1) || (tmp > 1024)) { + CdlParse::report_property_parse_error(interp, prop, + "Invalid priority value, priorities should be in the range 1 to 1024."); + } + } + } + + std::string data = prop->get_string(); + std::string target; + std::string deps; + std::string rules; + std::string error_msg; + + if (!CdlBuildableBody::split_custom_build_step(data, target, deps, rules, error_msg)) { + CdlParse::report_property_parse_error(interp, prop, "Invalid custom build step, " + error_msg); + } +} + +int +CdlBuildableBody::parse_make_object(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_make_object", "result %d"); + static char* options[] = { + "library:", + "priority:", + 0 + }; + + int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_MakeObject, options, + &parse_make_object_final_check); + + CYG_REPORT_RETVAL(result); + return result; +} + + +// ---------------------------------------------------------------------------- +// Syntax: object <file1> <file2> ... + +int +CdlBuildableBody::parse_object(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_object", "result %d"); + static char* options[] = { + "library:", + 0 + }; + + int result = CdlParse::parse_stringvector_property(interp, argc, argv, CdlPropertyId_Object, options, 0); + + CYG_REPORT_RETVAL(result); + return result; +} + +// ---------------------------------------------------------------------------- +// Syntax: build_proc { tcl code } + +int +CdlBuildableBody::parse_build_proc(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_build_proc", "result %d"); + + int result = CdlParse::parse_tclcode_property(interp, argc, argv, CdlPropertyId_BuildProc, 0, 0); + + CYG_REPORT_RETVAL(result); + return result; +} + +//}}} +//{{{ update_build_info() + +// ---------------------------------------------------------------------------- +// Most of the work is done in update_all_build_info(). The update_build_info() +// merely checks the active and enabled state first. +void +CdlBuildableBody::update_build_info(CdlBuildInfo_Loadable& build_info, std::string library) const +{ + CYG_REPORT_FUNCNAME("CdlBuildable::update_build_info"); + CYG_REPORT_FUNCARG2XV(this, &build_info); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITIONC("" != library); + + if (!is_active()) { + CYG_REPORT_RETURN(); + return; + } + + CdlConstValuable valuable = dynamic_cast<CdlConstValuable>(this); + if (0 != valuable) { + if (!valuable->is_enabled()) { + CYG_REPORT_RETURN(); + return; + } + } + + update_all_build_info(build_info, library); + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ update_all_build_info() + +// ---------------------------------------------------------------------------- +// There are four properties to be considered, each of which may occur +// multiple times: "compile", "object", "make_object", and "make". +// Each of these will result in separate additions to the build_info +// structure. + +void +CdlBuildableBody::update_all_build_info(CdlBuildInfo_Loadable& build_info, std::string package_library) const +{ + CYG_REPORT_FUNCNAME("CdlBuildable::update_all_build_info"); + CYG_REPORT_FUNCARG2XV(this, &build_info); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITIONC("" != package_library); + + // Get some information about the owning loadable first. + CdlLoadable loadable = get_owner(); + CYG_ASSERT_CLASSC(loadable); + std::string directory = loadable->get_directory(); + CYG_ASSERTC("" != directory); + CdlInterpreter interp = loadable->get_interpreter(); + CYG_ASSERT_CLASSC(interp); + + // The interpreter needs some information about the locations + // of various things. This code has to be kept in step with + // CdlLoadable::find_relative_file() + interp->set_variable("::cdl_topdir", get_toplevel()->get_directory()); + interp->set_variable("::cdl_pkgdir", directory); + + // For many packages the sources will reside in a src subdirectory. + // For simple packages the sources may live directly at the toplevel + bool has_src_subdir = loadable->has_subdirectory("src"); + + // NOTE: the object property is not yet supported + std::vector<CdlProperty> compile_properties; + get_properties(CdlPropertyId_Compile, compile_properties); + std::vector<CdlProperty> makeobject_properties; + get_properties(CdlPropertyId_MakeObject, makeobject_properties); + std::vector<CdlProperty> make_properties; + get_properties(CdlPropertyId_Make, make_properties); + std::vector<CdlProperty>::const_iterator prop_i; + + for (prop_i = compile_properties.begin(); prop_i != compile_properties.end(); prop_i++) { + CdlProperty_StringVector compile_prop = dynamic_cast<CdlProperty_StringVector>(*prop_i); + CYG_LOOP_INVARIANT_CLASSC(compile_prop); + + // Does this property have a library option? + std::string current_library = compile_prop->get_option("library"); + if ("" == current_library) { + current_library = package_library; + } + + const std::vector<std::string>& files = compile_prop->get_strings(); + std::vector<std::string>::const_iterator file_i; + + for (file_i = files.begin(); file_i != files.end(); file_i++) { + + // For each listed file, try to find it. If this is unsuccessful + // then assume that the file will be generated later on. + std::string path = loadable->find_relative_file(*file_i, "src"); + if ("" == path) { + if (has_src_subdir) { + path = "src/" + *file_i; + } else { + path = *file_i; + } + } + + // Now check whether or not the specified file is already present. + std::vector<CdlBuildInfo_Compile>::const_iterator info_i; + for (info_i = build_info.compiles.begin(); info_i != build_info.compiles.end(); info_i++) { + if ((current_library == info_i->library) && (path == info_i->source)) { + break; + } + } + if (info_i == build_info.compiles.end()) { + CdlBuildInfo_Compile new_info; + new_info.library = current_library; + new_info.source = path; + build_info.compiles.push_back(new_info); + } + } + } + + for (prop_i = makeobject_properties.begin(); prop_i != makeobject_properties.end(); prop_i++) { + CdlProperty_String prop = dynamic_cast<CdlProperty_String>(*prop_i); + CYG_LOOP_INVARIANT_CLASSC(prop); + + // Does thie property have a library option? + std::string current_library = prop->get_option("library"); + if ("" == current_library) { + current_library = package_library; + } + + // How about a priority field? The default priority for make_object is 100 + // We can rely on the validation done during the parsing process + cdl_int priority = 100; + std::string priority_option = prop->get_option("priority"); + if ("" != priority_option) { + Cdl::string_to_integer(priority_option, priority); + } + + // What we need now is the separate target, deps, and rules. These + // can be obtained via a utility. The raw data will have been validated + // already. + std::string raw_data = prop->get_string(); + std::string target; + std::string deps; + std::string rules; + std::string error_msg; + bool result; + + result = CdlBuildableBody::split_custom_build_step(raw_data, target, deps, rules, error_msg); + CYG_ASSERTC(true == result); + + // Construct a local object, then copy it into the vector + CdlBuildInfo_MakeObject local_copy; + local_copy.priority = priority; + local_copy.library = current_library; + local_copy.object = target; + local_copy.deps = deps; + local_copy.rules = rules; + + build_info.make_objects.push_back(local_copy); + } + + for (prop_i = make_properties.begin(); prop_i != make_properties.end(); prop_i++) { + CdlProperty_String prop = dynamic_cast<CdlProperty_String>(*prop_i); + CYG_LOOP_INVARIANT_CLASSC(prop); + + // Is there a priority field? The default priority for make is + // 300 We can rely on the validation done during the parsing + // process + cdl_int priority = 300; + std::string priority_option = prop->get_option("priority"); + if ("" != priority_option) { + Cdl::string_to_integer(priority_option, priority); + } + + // What we need now is the separate target, deps, and rules. These + // can be obtained via a utility. The raw data will have been validated + // already. + std::string raw_data = prop->get_string(); + std::string target; + std::string deps; + std::string rules; + std::string error_msg; + bool result; + + result = CdlBuildableBody::split_custom_build_step(raw_data, target, deps, rules, error_msg); + CYG_ASSERTC(true == result); + + // Construct a local object, then copy it into the vector + CdlBuildInfo_Make local_copy; + local_copy.priority = priority; + local_copy.target = target; + local_copy.deps = deps; + local_copy.rules = rules; + + build_info.makes.push_back(local_copy); + } + + CYG_REPORT_RETURN(); +} + +//}}} + +//}}} +//{{{ CdlBuildLoadableBody + +//{{{ Class variables + +// ---------------------------------------------------------------------------- +// This variable controls the default library that should be generated. +// Some applications may wish to override this. +char* CdlBuildLoadableBody::default_library_name = "libtarget.a"; + +// The pattern that should be used to identify header files. +// FIXME: this information should come out of a data file +char* CdlBuildLoadableBody::default_headers_glob_pattern = "*.h *.hxx *.inl *.si *.inc"; + +//}}} +//{{{ The simple stuff + +// ---------------------------------------------------------------------------- + +CdlBuildLoadableBody::CdlBuildLoadableBody() + : CdlLoadableBody() +{ + CYG_REPORT_FUNCNAME("CdlBuildLoadable:: default constructor"); + CYG_REPORT_FUNCARG1XV(this); + + // There is no data to initialize + cdlbuildloadablebody_cookie = CdlBuildLoadableBody_Magic; + CYGDBG_MEMLEAK_CONSTRUCTOR(); + + CYG_POSTCONDITION_THISC(); + CYG_REPORT_RETURN(); +} + +CdlBuildLoadableBody::~CdlBuildLoadableBody() +{ + CYG_REPORT_FUNCNAME("CdlBuildLoadable:: destructor"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + cdlbuildloadablebody_cookie = CdlBuildLoadableBody_Invalid; + CYGDBG_MEMLEAK_DESTRUCTOR(); + + CYG_REPORT_RETURN(); +} + +// ---------------------------------------------------------------------------- + +std::string +CdlBuildLoadableBody::get_class_name() const +{ + CYG_REPORT_FUNCNAME("CdlBuildLoadable::get_class_name"); + CYG_PRECONDITION_THISC(); + CYG_REPORT_RETURN(); + return "build_loadable"; +} + +// ---------------------------------------------------------------------------- + +bool +CdlBuildLoadableBody::check_this(cyg_assert_class_zeal zeal) const +{ + if (CdlBuildLoadableBody_Magic != cdlbuildloadablebody_cookie) { + return false; + } + CYGDBG_MEMLEAK_CHECKTHIS(); + return CdlContainerBody::check_this(zeal) && CdlNodeBody::check_this(zeal); +} + +//}}} +//{{{ Property parsers + +// ---------------------------------------------------------------------------- + +void +CdlBuildLoadableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers) +{ + CYG_REPORT_FUNCNAME("CdlBuildLoadable::add_property_parsers"); + + static CdlInterpreterCommandEntry commands[] = + { + CdlInterpreterCommandEntry("library", &CdlBuildLoadableBody::parse_library ), + CdlInterpreterCommandEntry("makefile", &CdlBuildLoadableBody::parse_makefile ), + CdlInterpreterCommandEntry("include_dir", &CdlBuildLoadableBody::parse_include_dir ), + CdlInterpreterCommandEntry("include_files", &CdlBuildLoadableBody::parse_include_files ), + CdlInterpreterCommandEntry("", 0 ) + }; + + for (int i = 0; commands[i].command != 0; i++) { + std::vector<CdlInterpreterCommandEntry>::const_iterator j; + for (j = parsers.begin(); j != parsers.end(); j++) { + if (commands[i].name == j->name) { + if (commands[i].command != j->command) { + CYG_FAIL("Property names are being re-used"); + } + break; + } + } + if (j == parsers.end()) { + parsers.push_back(commands[i]); + } + } + + CYG_REPORT_RETURN(); +} + +void +CdlBuildLoadableBody::check_properties(CdlInterpreter interp) +{ + CYG_REPORT_FUNCNAME("CdlBuildLoadable::check_properties"); + CYG_REPORT_FUNCARG2XV(this, interp); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(interp); + + CdlNodeBody::check_properties(interp); + + CYG_REPORT_RETURN(); +} + +// ---------------------------------------------------------------------------- +// syntax: library <filename> +// +// NOTE: there should probably be a check that the library name is in +// a valid format, i.e. libxxx.a + +int +CdlBuildLoadableBody::parse_library(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_library", "result %d"); + + int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Library, 0, 0); + + CYG_REPORT_RETVAL(result); + return result; +} + +// ---------------------------------------------------------------------------- +// syntax: makefile <filename> +// +// NOTE: possibly there should be a check that the makefile exists. +// Do we want to allow build_proc's to generate makefiles? +int +CdlBuildLoadableBody::parse_makefile(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_makefile", "result %d"); + + int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Makefile, 0, 0); + + CYG_REPORT_RETVAL(result); + return result; +} + +// ---------------------------------------------------------------------------- +// syntax: include_dir <directory name> +int +CdlBuildLoadableBody::parse_include_dir(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_include_dir", "result %d"); + + int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_IncludeDir, 0, 0); + + CYG_REPORT_RETVAL(result); + return result; +} + +// ---------------------------------------------------------------------------- +// Syntax: include_files <file1 file2 ...> +// +// This lists the header files that should be copied into the install tree +// as part of the build operation. In the absence of an include_files property +// there should be an include subdirectory, and all files in that subdirectory +// are assumed to be exportable headers. +// +// NOTE: add a finalizer to check that the files exist or get created. + +int +CdlBuildLoadableBody::parse_include_files(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_include_files", "result %d"); + + int result = CdlParse::parse_stringvector_property(interp, argc, argv, CdlPropertyId_IncludeFiles, 0, 0); + + CYG_REPORT_RETVAL(result); + return result; +} + +//}}} +//{{{ update_build_info() + +// ---------------------------------------------------------------------------- +// This utility routine takes care of filling in a Buildinfo_Loadable +// structure with the appropriate header file information. This involves +// the following: +// +// 1) there may be an include_dir property. This affects the destination +// of any header files that are listed. +// +// 2) the loadable may or may not have an include subdirectory. For +// non-trivial packages the include subdirectory provides a clean +// way of separating interface and implementation. For simple +// packages it is too heavyweight. +// +// 3) there may be one or more include_files property. If so then these +// specify all the files that should be exported. +// +// 4) otherwise if there is an include subdirectory then we need to +// know all the header files in that subdirectory. A Tcl script is +// used for this. +// +// 5) otherwise all the header files below the package directory itself +// are of interest. + +static void +update_header_file_info(CdlConstBuildLoadable loadable, CdlBuildInfo_Loadable& build_info) +{ + CYG_REPORT_FUNCNAME("update_header_file_info"); + CYG_REPORT_FUNCARG2XV(loadable, &build_info); + CYG_PRECONDITION_CLASSC(loadable); + + std::string dest_dir = ""; + CdlProperty include_dir_prop = loadable->get_property(CdlPropertyId_IncludeDir); + if (0 != include_dir_prop) { + CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(include_dir_prop); + CYG_ASSERT_CLASSC(strprop); + dest_dir = strprop->get_string(); + } + + bool has_include_subdir = loadable->has_subdirectory("include"); + + std::vector<CdlProperty> include_file_properties; + loadable->get_properties(CdlPropertyId_IncludeFiles, include_file_properties); + if (include_file_properties.size() > 0) { + std::vector<CdlProperty>::const_iterator prop_i; + for (prop_i = include_file_properties.begin(); prop_i != include_file_properties.end(); prop_i++) { + + CdlProperty_StringVector strvprop = dynamic_cast<CdlProperty_StringVector>(*prop_i); + CYG_ASSERT_CLASSC(strvprop); + + const std::vector<std::string>& filenames = strvprop->get_strings(); + std::vector<std::string>::const_iterator file_i; + for (file_i = filenames.begin(); file_i != filenames.end(); file_i++) { + std::string path = loadable->find_relative_file(*file_i, "include"); + // Assume that the header file will be generated by a build_proc + if ("" == path) { + if (has_include_subdir) { + path = "include/" + *file_i; + } else { + path = *file_i; + } + } + CdlBuildInfo_Header local_copy; + local_copy.source = path; + local_copy.destination = ""; + if ("" != dest_dir) { + local_copy.destination = dest_dir + "/"; + } + // At this stage "path" may begin with "include/", which should not + // be present in the destination. + const char* tmp = path.c_str(); + if (0 == strncmp("include/", tmp, 8)) { + local_copy.destination += &(tmp[8]); + } else { + local_copy.destination += path; + } + build_info.headers.push_back(local_copy); + } + } + CYG_REPORT_RETURN(); + return; + } + + // It is necessary to search for the appropriate files. + CdlInterpreter interp = loadable->get_interpreter(); + std::string path = loadable->get_toplevel()->get_directory() + "/" + loadable->get_directory(); + if (has_include_subdir) { + std::vector<std::string> files; + std::vector<std::string>::const_iterator file_i; + interp->locate_all_files(path + "/include", files); + for (file_i = files.begin(); file_i != files.end(); file_i++) { + // NOTE: for now discard any header files in the pkgconf subdirectory + if (0 == strncmp("pkgconf/", file_i->c_str(), 8)) { + continue; + } + CdlBuildInfo_Header local_copy; + local_copy.source = "include/" + *file_i; + local_copy.destination = ""; + if ("" != dest_dir) { + local_copy.destination = dest_dir + "/"; + } + local_copy.destination += *file_i; + build_info.headers.push_back(local_copy); + } + } else { + // Look for all header files, which for now means files with + // a .h, .hxx, .inl or .inc extension. + // FIXME: the definition of what constitutes a header file + // should not be hard-wired here. + std::vector<std::string> files; + std::vector<std::string>::const_iterator file_i; + interp->locate_all_files(path, files); + for (file_i = files.begin(); file_i != files.end(); file_i++) { + + // Problems with libstdc++ versions, use C comparisons instead. + const char* c_string = file_i->c_str(); + unsigned int len = strlen(c_string); + if (((len >= 2) && (0 == strncmp(c_string + len - 2, ".h", 2))) || + ((len >= 4) && (0 == strncmp(c_string + len - 4, ".hxx", 4))) || + ((len >= 4) && (0 == strncmp(c_string + len - 4, ".inl", 4))) || + ((len >= 4) && (0 == strncmp(c_string + len - 4, ".inc", 4)))) { + + CdlBuildInfo_Header local_copy; + local_copy.source = *file_i; + local_copy.destination = ""; + if ("" != dest_dir) { + local_copy.destination = dest_dir + "/"; + } + local_copy.destination += *file_i; + build_info.headers.push_back(local_copy); + } + } + } + + CYG_REPORT_RETURN(); + return; +} + +// ---------------------------------------------------------------------------- +// Updating a loadable build's info involves two steps. First, there +// is some information associated with the loadable as a whole such as +// header files. Second, each buildable in the loadable (including itself) +// may contain properties such as compile etc. This is all handled via +// a CdlBuildable member function. + +void +CdlBuildLoadableBody::update_build_info(CdlBuildInfo& build_info) const +{ + CYG_REPORT_FUNCNAME("CdlBuildLoadable::update_build_info"); + CYG_REPORT_FUNCARG2XV(this, &build_info); + CYG_PRECONDITION_THISC(); + + // It is not possible to disable a loadable itself: either the + // loadable is present or it is not (although in some cases users + // may be able to change versions). However, because of reparenting + // it is possible for a loadable to be below a disabled container, + // and hence it is still necessary to check whether or not the + // loadable is active. + if (!is_active()) { + CYG_REPORT_RETURN(); + return; + } + + // Time to add a new CdlBuildInfo_Loadable object to the current + // vector. The name and directory can be filled in straightaway, + // the vectors will all be initialized to empty. + CdlBuildInfo_Loadable tmp_info; + build_info.entries.push_back(tmp_info); + CdlBuildInfo_Loadable& this_info = *(build_info.entries.rbegin()); + this_info.name = get_name(); + this_info.directory = get_directory(); + + // Take care of the header files + update_header_file_info(this, this_info); + + // Work out the library name appropriate for this loadable. + // There may be a library property, otherwise the global default + // should be used. + std::string loadable_library = default_library_name; + if (has_property(CdlPropertyId_Library)) { + CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(get_property(CdlPropertyId_Library)); + loadable_library = strprop->get_string(); + } + + const std::vector<CdlNode>& contents = get_owned(); + std::vector<CdlNode>::const_iterator node_i; + for (node_i = contents.begin(); node_i != contents.end(); node_i++) { + CdlBuildable buildable = dynamic_cast<CdlBuildable>(*node_i); + if (0 != buildable) { + buildable->update_build_info(this_info, loadable_library); + } + } + + CYG_REPORT_RETURN(); +} + +// This is much the same as the above, but there is no test for +// active either at the loadable level or for the individual buildables. +void +CdlBuildLoadableBody::update_all_build_info(CdlBuildInfo& build_info) const +{ + CYG_REPORT_FUNCNAME("CdlBuildLoadable::update_all_build_info"); + CYG_REPORT_FUNCARG2XV(this, &build_info); + CYG_PRECONDITION_THISC(); + + CdlBuildInfo_Loadable tmp_info; + build_info.entries.push_back(tmp_info); + CdlBuildInfo_Loadable& this_info = *(build_info.entries.rbegin()); + this_info.name = get_name(); + this_info.directory = get_directory(); + + std::string loadable_library = default_library_name; + if (has_property(CdlPropertyId_Library)) { + CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(get_property(CdlPropertyId_Library)); + loadable_library = strprop->get_string(); + } + + const std::vector<CdlNode>& contents = get_owned(); + std::vector<CdlNode>::const_iterator node_i; + for (node_i = contents.begin(); node_i != contents.end(); node_i++) { + CdlBuildable buildable = dynamic_cast<CdlBuildable>(*node_i); + if (0 != buildable) { + buildable->update_build_info(this_info, loadable_library); + } + } + + CYG_REPORT_RETURN(); + CYG_REPORT_RETURN(); +} + +//}}} + +//}}} +//{{{ CdlDefinableBody + +//{{{ Basics + +// ---------------------------------------------------------------------------- + +CdlDefinableBody::CdlDefinableBody() +{ + CYG_REPORT_FUNCNAME("CdlDefinable:: default constructor"); + CYG_REPORT_FUNCARG1XV(this); + + // There is no data to initialize + cdldefinablebody_cookie = CdlDefinableBody_Magic; + CYGDBG_MEMLEAK_CONSTRUCTOR(); + + CYG_POSTCONDITION_THISC(); + CYG_REPORT_RETURN(); +} + +CdlDefinableBody::~CdlDefinableBody() +{ + CYG_REPORT_FUNCNAME("CdlDefinable:: destructor"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + cdldefinablebody_cookie = CdlDefinableBody_Invalid; + CYGDBG_MEMLEAK_DESTRUCTOR(); + + CYG_REPORT_RETURN(); +} + +// ---------------------------------------------------------------------------- + +std::string +CdlDefinableBody::get_class_name() const +{ + CYG_REPORT_FUNCNAME("CdlDefinable::get_class_name"); + CYG_PRECONDITION_THISC(); + CYG_REPORT_RETURN(); + return "definable"; +} + +// ---------------------------------------------------------------------------- + +bool +CdlDefinableBody::check_this(cyg_assert_class_zeal zeal) const +{ + if (CdlDefinableBody_Magic != cdldefinablebody_cookie) { + return false; + } + CYGDBG_MEMLEAK_CHECKTHIS(); + return CdlNodeBody::check_this(zeal); +} + +//}}} +//{{{ add_property_parser() and check_properties() + +// ---------------------------------------------------------------------------- + +void +CdlDefinableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers) +{ + CYG_REPORT_FUNCNAME("CdlDefinable::add_property_parsers"); + + static CdlInterpreterCommandEntry commands[] = + { + CdlInterpreterCommandEntry("no_define", &parse_no_define ), + CdlInterpreterCommandEntry("define", &parse_define ), + CdlInterpreterCommandEntry("define_format", &parse_define_format ), + CdlInterpreterCommandEntry("define_proc", &parse_define_proc ), + CdlInterpreterCommandEntry("if_define", &parse_if_define ), + CdlInterpreterCommandEntry("", 0 ) + }; + + for (int i = 0; commands[i].command != 0; i++) { + std::vector<CdlInterpreterCommandEntry>::const_iterator j; + for (j = parsers.begin(); j != parsers.end(); j++) { + if (commands[i].name == j->name) { + if (commands[i].command != j->command) { + CYG_FAIL("Property names are being re-used"); + } + break; + } + } + if (j == parsers.end()) { + parsers.push_back(commands[i]); + } + } + CdlNodeBody::add_property_parsers(parsers); + + CYG_REPORT_RETURN(); +} + +void +CdlDefinableBody::check_properties(CdlInterpreter interp) +{ + CYG_REPORT_FUNCNAME("CdlDefinable::check_properties"); + CYG_REPORT_FUNCARG2XV(this, interp); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(interp); + + // There should be at most one each of no_define and define_format. + if (count_properties(CdlPropertyId_NoDefine) > 1) { + CdlParse::report_error(interp, "There should be at most one no_define property."); + } + if (count_properties(CdlPropertyId_DefineFormat) > 1) { + CdlParse::report_error(interp, "There should be at most one define_format property."); + } + if (has_property(CdlPropertyId_NoDefine) && has_property(CdlPropertyId_DefineFormat)) { + CdlParse::report_error(interp, "The no_define and define_format properties are mutually exclusive."); + } + // FIXME: the define_format property only makes sense for certain + // flavors. However the flavor property may not have been processed yet. + + CdlNodeBody::check_properties(interp); + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ Definable properties + +// ---------------------------------------------------------------------------- +// Syntax: no_define +int +CdlDefinableBody::parse_no_define(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_no_define", "result %d"); + + int result = CdlParse::parse_minimal_property(interp, argc, argv, CdlPropertyId_NoDefine, 0, 0); + + CYG_REPORT_RETVAL(result); + return result; +} + +// ---------------------------------------------------------------------------- +// syntax: define <symbol> + +// The argument to "define" should be a valid C preprocessor symbol. +static void +parse_define_final_check(CdlInterpreter interp, CdlProperty_String prop) +{ + CYG_REPORT_FUNCNAME("parse_define_final_check"); + CYG_PRECONDITION_CLASSC(prop); + CYG_PRECONDITION_CLASSC(interp); + + const std::string& str = prop->get_string(); + + if (!Cdl::is_valid_c_preprocessor_symbol(str)) { + CdlParse::report_property_parse_error(interp, prop, str + " is not a valid C preprocessor symbol"); + } + + // There may be a file option. At this stage the only valid filename + // that can be used here is system.h + std::string file_option = prop->get_option("file"); + if (("" != file_option) && ("system.h" != file_option)) { + CdlParse::report_property_parse_error(interp, prop, "Invalid -file option " + file_option); + } + + // FIXME: validate the format string + + CYG_REPORT_RETURN(); +} + +int +CdlDefinableBody::parse_define(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_define", "result %d"); + + static char* options[] = { + "file:", + "format:", + 0 + }; + int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Define, options, &parse_define_final_check); + + CYG_REPORT_RETVAL(result); + return result; +} + + +// ---------------------------------------------------------------------------- +// syntax: define_format <string> +// +// FIXME: it is possible to apply some checks to the string, e.g. that there +// is only one conversion operation. +// +// FIXME: also check that the flavor is sensible, define_format has no effect +// for none or bool +// +// FIXME: enforce mutual exclusion with no_define + +int +CdlDefinableBody::parse_define_format(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_format", "result %d"); + + int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_DefineFormat, 0, 0); + + CYG_REPORT_RETVAL(result); + return result; +} +// ---------------------------------------------------------------------------- +// syntax: define_proc <tclcode> +int +CdlDefinableBody::parse_define_proc(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_define_proc", "result %d"); + + int result = CdlParse::parse_tclcode_property(interp, argc, argv, CdlPropertyId_DefineProc, 0, 0); + + CYG_REPORT_RETVAL(result); + return result; +} + +// ---------------------------------------------------------------------------- +// Syntax: if_define sym1 sym2 + +static void +parse_if_define_final_check(CdlInterpreter interp, CdlProperty_StringVector prop) +{ + CYG_REPORT_FUNCNAME("parse_if_define_final_check"); + CYG_PRECONDITION_CLASSC(interp); + CYG_PRECONDITION_CLASSC(prop); + + // There should be exactly two entries in the vector, and both of them should be + // valid preprocessor symbols. + const std::vector<std::string>& strings = prop->get_strings(); + + if (2 != strings.size()) { + CdlParse::report_property_parse_error(interp, prop, "There should be exactly two arguments."); + } + if (!Cdl::is_valid_c_preprocessor_symbol(strings[0])) { + CdlParse::report_property_parse_error(interp, prop, strings[0] + " is not a valid C preprocessor symbol."); + } + if (!Cdl::is_valid_c_preprocessor_symbol(strings[1])) { + CdlParse::report_property_parse_error(interp, prop, strings[1] + " is not a valid C preprocessor symbol."); + } + + // There may be a file option. At this stage the only valid filename + // that can be used here is system.h + std::string file_option = prop->get_option("file"); + if (("" != file_option) && ("system.h" != file_option)) { + CdlParse::report_property_parse_error(interp, prop, "Invalid -file option " + file_option); + } +} + +int +CdlDefinableBody::parse_if_define(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_if_define", "result %d"); + + char* options[] = { + "file:", + 0 + }; + int result = CdlParse::parse_stringvector_property(interp, argc, argv, CdlPropertyId_IfDefine, options, + &parse_if_define_final_check); + + CYG_REPORT_RETVAL(result); + return result; +} + +//}}} +//{{{ generate_config_header() + +// ---------------------------------------------------------------------------- +// This code needs to allow for the following properties. +// +// 1) no_define. This suppresses the default #define generation. +// +// 2) define_format <format_string. +// +// 3) define [-file <filename>][-format <format_string>] symbol +// +// 4) define_proc +// +// 5) if_define + +void +CdlDefinableBody::generate_config_header(Tcl_Channel this_hdr, Tcl_Channel system_h) const + throw(CdlInputOutputException, std::bad_alloc) +{ + CYG_REPORT_FUNCNAME("CdlDefinable::generate_config_header"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CdlLoadable loadable = get_owner(); + CdlInterpreter interp = loadable->get_interpreter(); + + // This definable is known to be active. However it may or may not be enabled. + CYG_PRECONDITIONC(is_active()); + + std::string name = get_name(); + CdlValueFlavor flavor = CdlValueFlavor_Bool; + std::string value = "1"; + CdlConstValuable valuable = dynamic_cast<CdlConstValuable>(this); + if (0 != valuable) { + // It is always possible to check the enabled() flag. + if (!valuable->is_enabled()) { + CYG_REPORT_RETURN(); + return; + } + // The value is only valid for BoolData and Data flavors, and may + // not have been provided. If there is no value then this option + // should not generate a #define + flavor = valuable->get_flavor(); + if ((CdlValueFlavor_BoolData == flavor) || (CdlValueFlavor_Data == flavor)) { + value = valuable->get_value(); + } + } + + // Flavor and value are now both set to sensible strings. + // First, check the no_define property. If this is present then the default + // #define generation should be suppressed. + if (!has_property(CdlPropertyId_NoDefine)) { + + // OK, it is necessary to generate at least one #define. + // If this node is actually a loadable then the #define should go + // into system.h, otherwise into the current header + Tcl_Channel chan = this_hdr; + if (dynamic_cast<CdlConstLoadable>((CdlConstNode)this) == loadable) { + chan = system_h; + } + + // For flavors None and Bool, there should be just one #define + if ((CdlValueFlavor_None == flavor) || (CdlValueFlavor_Bool == flavor)) { + std::string define = "#define " + name + " 1\n"; + Tcl_Write(chan, const_cast<char*>(define.c_str()), -1); + } else { + // If there is a format string then that controls the default + // value display. + if (!has_property(CdlPropertyId_DefineFormat)) { + std::string define = "#define " + name + " " + value + "\n"; + Tcl_Write(chan, const_cast<char*>(define.c_str()), -1); + } else { + CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(get_property(CdlPropertyId_DefineFormat)); + CYG_ASSERT_CLASSC(strprop); + std::string format = strprop->get_string(); + std::string cmd = "return \"#define " + name + " [format " + format + " " + value + "]\n\""; + std::string define; + if (TCL_OK != interp->eval(cmd, define)) { + throw CdlInputOutputException("Internal error executing tcl fragment to process define_format property"); + } + Tcl_Write(chan, const_cast<char*>(define.c_str()), -1); + } + + // There may also be a separate #define of the form <name>_<value>, + // if that is a valid preprocessor symbol. + std::string tmp = name + "_" + value; + if (Cdl::is_valid_c_preprocessor_symbol(tmp)) { + tmp = "#define "+ tmp + "\n"; + Tcl_Write(chan, const_cast<char*>(tmp.c_str()), -1); + } + } + } + + // Next, check for any additional define properties + std::vector<CdlProperty> define_props; + get_properties(CdlPropertyId_Define, define_props); + std::vector<CdlProperty>::const_iterator prop_i; + for (prop_i = define_props.begin(); prop_i != define_props.end(); prop_i++) { + CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(*prop_i); + CYG_ASSERT_CLASSC(strprop); + std::string symbol = strprop->get_string(); + + std::string file = strprop->get_option("file"); + Tcl_Channel chan = this_hdr; + if (("" != file) && ("system.h" == file)) { + chan = system_h; + } + + if ((CdlValueFlavor_None == flavor) || (CdlValueFlavor_Bool == flavor)) { + std::string define = "#define " + symbol + " 1\n"; + Tcl_Write(chan, const_cast<char*>(define.c_str()), -1); + } else { + std::string format = strprop->get_option("format"); + if ("" == format) { + std::string define = "#define " + symbol + " " + value + "\n"; + Tcl_Write(chan, const_cast<char*>(define.c_str()), -1); + } else { + std::string cmd = "return \"#define " + symbol + " [format " + format + " " + value + "]\n\""; + std::string define; + if (TCL_OK != interp->eval(cmd, define)) { + throw CdlInputOutputException("Internal error executing tcl fragment to process format option"); + } + Tcl_Write(chan, const_cast<char*>(define.c_str()), -1); + } + + std::string tmp = symbol + "_" + value; + if (Cdl::is_valid_c_preprocessor_symbol(tmp)) { + tmp = "#define " + tmp + "\n"; + Tcl_Write(chan, const_cast<char*>(tmp.c_str()), -1); + } + } + } + + // Now check for if_define properties + std::vector<CdlProperty> if_define_props; + get_properties(CdlPropertyId_IfDefine, if_define_props); + for (prop_i = if_define_props.begin(); prop_i != if_define_props.end(); prop_i++) { + CdlProperty_StringVector strprop = dynamic_cast<CdlProperty_StringVector>(*prop_i); + CYG_ASSERT_CLASSC(strprop); + CYG_ASSERTC(2 == strprop->get_number_of_strings()); + + std::string sym1 = strprop->get_string(0); + std::string sym2 = strprop->get_string(1); + + Tcl_Channel chan = this_hdr; + std::string file = strprop->get_option("file"); + if (("" != file) && ("system.h" == file)) { + chan = system_h; + } + std::string data = "#ifdef " + sym1 + "\n# define " + sym2 + " 1\n#endif\n"; + Tcl_Write(chan, const_cast<char*>(data.c_str()), -1); + } + + // And define_proc properties + std::vector<CdlProperty> define_proc_props; + get_properties(CdlPropertyId_DefineProc, define_proc_props); + for (prop_i = define_proc_props.begin(); prop_i != define_proc_props.end(); prop_i++) { + CdlProperty_TclCode codeprop = dynamic_cast<CdlProperty_TclCode>(*prop_i); + CYG_ASSERT_CLASSC(codeprop); + + cdl_tcl_code code = codeprop->get_code(); + std::string result; + if (TCL_OK != interp->eval(code, result)) { + throw CdlInputOutputException("Error evaluating define_proc property for " + name + "\n" + result); + } + } + + + CYG_REPORT_RETURN(); +} + +//}}} + +//}}} +//{{{ CdlDefineLoadableBody + +//{{{ Basics + +// ---------------------------------------------------------------------------- + +CdlDefineLoadableBody::CdlDefineLoadableBody() +{ + CYG_REPORT_FUNCNAME("CdlDefineLoadable:: default constructor"); + CYG_REPORT_FUNCARG1XV(this); + + cdldefineloadablebody_cookie = CdlDefineLoadableBody_Magic; + CYGDBG_MEMLEAK_CONSTRUCTOR(); + + CYG_POSTCONDITION_THISC(); + CYG_REPORT_RETURN(); +} + +CdlDefineLoadableBody::~CdlDefineLoadableBody() +{ + CYG_REPORT_FUNCNAME("CdlDefineLoadable:: destructor"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + cdldefineloadablebody_cookie = CdlDefineLoadableBody_Invalid; + CYGDBG_MEMLEAK_DESTRUCTOR(); + + CYG_REPORT_RETURN(); +} + +// ---------------------------------------------------------------------------- + +std::string +CdlDefineLoadableBody::get_class_name() const +{ + CYG_REPORT_FUNCNAME("CdlDefineLoadable::get_class_name"); + CYG_PRECONDITION_THISC(); + CYG_REPORT_RETURN(); + return "define_loadable"; +} + +// ---------------------------------------------------------------------------- + +bool +CdlDefineLoadableBody::check_this(cyg_assert_class_zeal zeal) const +{ + if (CdlDefineLoadableBody_Magic != cdldefineloadablebody_cookie) { + return false; + } + CYGDBG_MEMLEAK_CHECKTHIS(); + return CdlLoadableBody::check_this(zeal) && CdlNodeBody::check_this(zeal); +} + +//}}} +//{{{ Property parsing + +// ---------------------------------------------------------------------------- + +void +CdlDefineLoadableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers) +{ + CYG_REPORT_FUNCNAME("CdlDefineLoadable::add_property_parsers"); + + static CdlInterpreterCommandEntry commands[] = + { + CdlInterpreterCommandEntry("define_header", &parse_define_header), + CdlInterpreterCommandEntry("", 0 ) + }; + + for (int i = 0; commands[i].command != 0; i++) { + std::vector<CdlInterpreterCommandEntry>::const_iterator j; + for (j = parsers.begin(); j != parsers.end(); j++) { + if (commands[i].name == j->name) { + if (commands[i].command != j->command) { + CYG_FAIL("Property names are being re-used"); + } + break; + } + } + if (j == parsers.end()) { + parsers.push_back(commands[i]); + } + } + CdlNodeBody::add_property_parsers(parsers); + + CYG_REPORT_RETURN(); +} + +void +CdlDefineLoadableBody::check_properties(CdlInterpreter interp) +{ + CYG_REPORT_FUNCNAME("CdlDefineLoadable::check_properties"); + CYG_REPORT_FUNCARG2XV(this, interp); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(interp); + + // There should be at most one define_header property + int count = count_properties(CdlPropertyId_DefineHeader); + if (count> 1) { + CdlParse::report_error(interp, "There should be at most one define_header property."); + } + // FIXME: filename validation + + CdlNodeBody::check_properties(interp); + + CYG_REPORT_RETURN(); +} + +// ---------------------------------------------------------------------------- +// syntax: define_header <header file name> +int +CdlDefineLoadableBody::parse_define_header(CdlInterpreter interp, int argc, char** argv) +{ + CYG_REPORT_FUNCNAMETYPE("parse_define_header", "result %d"); + + int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_DefineHeader, 0, 0); + + CYG_REPORT_RETVAL(result); + return result; +} + +//}}} +//{{{ generate_config_header() + +// ---------------------------------------------------------------------------- +void +CdlDefineLoadableBody::generate_config_header(Tcl_Channel this_hdr, Tcl_Channel system_h) const + throw(CdlInputOutputException, std::bad_alloc) +{ + CYG_REPORT_FUNCNAME("CdlDefineLoadable::generate_config_header"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CdlInterpreter interp = get_interpreter(); + Tcl_RegisterChannel(interp->get_tcl_interpreter(), this_hdr); + Tcl_RegisterChannel(interp->get_tcl_interpreter(), system_h); + + try { + interp->set_variable("::cdl_header", Tcl_GetChannelName(this_hdr)); + interp->set_variable("::cdl_system_header", Tcl_GetChannelName(system_h)); + + const std::vector<CdlNode>& contents = get_owned(); + std::vector<CdlNode>::const_iterator node_i; + for (node_i = contents.begin(); node_i != contents.end(); node_i++) { + CdlDefinable definable = dynamic_cast<CdlDefinable>(*node_i); + if (0 == definable) { + continue; + } + if (!definable->is_active()) { + continue; + } + definable->generate_config_header(this_hdr, system_h); + } + + interp->unset_variable("::cdl_header"); + interp->unset_variable("::cdl_system_header"); + } catch(...) { + Tcl_UnregisterChannel(interp->get_tcl_interpreter(), this_hdr); + Tcl_UnregisterChannel(interp->get_tcl_interpreter(), system_h); + throw; + } + + Tcl_UnregisterChannel(interp->get_tcl_interpreter(), this_hdr); + Tcl_UnregisterChannel(interp->get_tcl_interpreter(), system_h); + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ get_config_headers() + +// ---------------------------------------------------------------------------- +// What header file should be generated for this loadable? +// +// If there is a define_header property then this should be used. +// Otherwise a filename is constructed from the loadable's name. + +std::string +CdlDefineLoadableBody::get_config_header() const +{ + CYG_REPORT_FUNCNAME("CdlDefineLoadable::get_config_headers"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + std::string result = ""; + CdlProperty prop = get_property(CdlPropertyId_DefineHeader); + if (0 != prop) { + CdlProperty_String string_prop = dynamic_cast<CdlProperty_String>(prop); + CYG_ASSERT_CLASSC(string_prop); + result = string_prop->get_string(); + } else { + std::string tmp = get_name(); + result = Cdl::get_short_form(tmp); + result += ".h"; + } + CYG_REPORT_RETURN(); + return result; +} + +//}}} + +//}}} +//{{{ CdlToplevel + +//{{{ CdlToplevel::get_build_info() + +// ---------------------------------------------------------------------------- +// Essentially this code involves iterating over the loadables vector, +// looking for BuildLoadables and invoking their update_build_info() +// member function. In addition, if there is currently some data in +// the build_info vector (probably from a previous call) then that +// must be cleared. + +void +CdlToplevelBody::get_build_info(CdlBuildInfo& build_info) +{ + CYG_REPORT_FUNCNAME("CdlToplevel::get_build_info"); + CYG_REPORT_FUNCARG2XV(this, &build_info); + CYG_PRECONDITION_THISC(); + + if (0 != build_info.entries.size()) { + build_info.entries.clear(); + } + + const std::vector<CdlLoadable>& loadables = get_loadables(); + std::vector<CdlLoadable>::const_iterator load_i; + for (load_i = loadables.begin(); load_i != loadables.end(); load_i++) { + CdlConstBuildLoadable bl = dynamic_cast<CdlConstBuildLoadable>(*load_i); + if (0 != bl) { + bl->update_build_info(build_info); + } + } + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ CdlToplevel::get_all_build_info() + +// ---------------------------------------------------------------------------- +// This is just like get_build_info(), but calls a different +// BuildLoadable member. + +void +CdlToplevelBody::get_all_build_info(CdlBuildInfo& build_info) +{ + CYG_REPORT_FUNCNAME("CdlToplevel::get_all_build_info"); + CYG_REPORT_FUNCARG2XV(this, &build_info); + CYG_PRECONDITION_THISC(); + + if (0 != build_info.entries.size()) { + build_info.entries.clear(); + } + + const std::vector<CdlLoadable>& loadables = get_loadables(); + std::vector<CdlLoadable>::const_iterator load_i; + for (load_i = loadables.begin(); load_i != loadables.end(); load_i++) { + CdlConstBuildLoadable bl = dynamic_cast<CdlConstBuildLoadable>(*load_i); + if (0 != bl) { + bl->update_all_build_info(build_info); + } + } + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ CdlToplevel::generate_config_headers() + +// ---------------------------------------------------------------------------- +// Generating the config headers. This involves the following steps: +// +// 1) for every DefineLoadable, find out what header file should +// be generated. Note that some loadables may share a header file. +// +// 2) create a temporary version of system.h. Note that it is not +// a good idea to just overwrite an existing system.h, chances +// are pretty good that the file will not have changed, and +// updating it unnecessarily will result in unnecessary rebuilds +// due to header file dependencies. +// +// 3) for each file that should be generated, create a temporary +// version and allow all applicable loadables to update it. + +// A utility to compare two files and do the right thing. +// This requires some simple Tcl code. +static void +compare_and_copy(CdlInterpreter interp, std::string file1, std::string file2) +{ + CYG_REPORT_FUNCNAME("compare_and_copy"); + CYG_PRECONDITION_CLASSC(interp); + CYG_PRECONDITIONC("" != file1); + CYG_PRECONDITIONC("" != file2); + CYG_PRECONDITIONC(file1 != file2); + + static char compare_and_copy_script[] = "\ +if {[file exists \"$::cdl_compare_and_copy_file2\"] == 0} { \n\ + file rename -- \"$::cdl_compare_and_copy_file1\" \"$::cdl_compare_and_copy_file2\" \n\ + return \n\ +} \n\ +set fd [open \"$::cdl_compare_and_copy_file1\" r] \n\ +set data1 [read $fd] \n\ +close $fd \n\ +set fd [open \"$::cdl_compare_and_copy_file2\" r] \n\ +set data2 [read $fd] \n\ +close $fd \n\ +if {$data1 == $data2} { \n\ + file delete \"$::cdl_compare_and_copy_file1\" \n\ +} else { \n\ + file rename -force -- \"$::cdl_compare_and_copy_file1\" \"$::cdl_compare_and_copy_file2\" \n\ +} \n\ +"; + + interp->set_variable("::cdl_compare_and_copy_file1", file1); + interp->set_variable("::cdl_compare_and_copy_file2", file2); + std::string tcl_result; + if (TCL_OK != interp->eval(compare_and_copy_script, tcl_result)) { + throw CdlInputOutputException("internal error manipulating temporary header " + file1 + " and target " + file2 + + "\n" + tcl_result); + } +} + +void +CdlToplevelBody::generate_config_headers(std::string directory) + throw(CdlInputOutputException, std::bad_alloc) +{ + CYG_REPORT_FUNCNAME("CdlToplevel::generate_config_headers"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + CYG_ASSERTC("" != directory); + + // Replace any backslashes in the path with forward slashes. The + // latter are used throughout the library + // NOTE: this is not i18n-friendly. + for (unsigned int i = 0; i < directory.size(); i++) { + if ('\\' == directory[i]) { + directory[i] = '/'; + } + } + + CdlInterpreter interp = get_interpreter(); + std::string tcl_result; + if ((TCL_OK != interp->eval("file isdirectory \"" + directory + "\"", tcl_result)) || + (tcl_result != "1")) { + throw CdlInputOutputException("target " + directory + " is not a valid existing directory."); + } + + std::vector<std::pair<CdlDefineLoadable, std::string> > headers; + const std::vector<CdlLoadable>& loadables = get_loadables(); + std::vector<CdlLoadable>::const_iterator load_i; + for (load_i = loadables.begin(); load_i != loadables.end(); load_i++) { + CdlDefineLoadable tmp = dynamic_cast<CdlDefineLoadable>(*load_i); + if (0 != tmp) { + std::string hdr = tmp->get_config_header(); + headers.push_back(std::make_pair(tmp, hdr)); + } + } + + static char banner_format[] = +"#ifndef CYGONCE_PKGCONF_%s\n\ +#define CYGONCE_PKGCONF_%s\n\ +/*\n\ + * File <pkgconf/%s>\n\ + *\n\ + * This file is generated automatically by the configuration\n\ + * system. It should not be edited. Any changes to this file\n\ + * may be overwritten.\n\ + */\n\ +\n"; +#ifdef _WIN32 + // Create three channels which Tcl will use for standard streams + // if these streams do not already exist. This avoids a Tcl + // problem which can prevent closure of system.h. (FIXME) + Tcl_Channel stdin_chan = Tcl_OpenFileChannel(interp->get_tcl_interpreter(), "nul", "w", 0666); + Tcl_Channel stdout_chan = Tcl_OpenFileChannel(interp->get_tcl_interpreter(), "nul", "w", 0666); + Tcl_Channel stderr_chan = Tcl_OpenFileChannel(interp->get_tcl_interpreter(), "nul", "w", 0666); + Tcl_RegisterChannel(0, stdin_chan); + Tcl_RegisterChannel(0, stdout_chan); + Tcl_RegisterChannel(0, stderr_chan); +#endif + // Assume for now that files __libcdl_file1 and __libcdl_file2 are + // legal on all platforms of interest, and that nobody is going to + // export to these. + std::string system_h_name = directory + "/__libcdl_file1"; + Tcl_Channel system_h = Tcl_OpenFileChannel(interp->get_tcl_interpreter(), + const_cast<char*>(system_h_name.c_str()), "w", 0666); + if (0 == system_h) { + throw CdlInputOutputException("Unable to open file " + system_h_name + "\n" + + interp->get_result()); + } + // The channel will be registered and unregistered in several + // different interpreters. This call prevents the channel from + // disappearing prematurely. + Tcl_RegisterChannel(0, system_h); + + // Make sure that this operation is undone if necessary. + try { + // Now fill in system.h with the appropriate data. Start with the banner. + char local_buf[512]; + sprintf(local_buf, banner_format, "SYSTEM_H", "SYSTEM_H", "system.h"); + Tcl_Write(system_h, local_buf, -1); + + // The rest of system.h will be filled in by the following loop. + // + // Next, walk down the previously constructed headers vector, + // create appropriate files, and let each DefineLoadable fill + // in the file for itself. + std::vector<std::pair<CdlDefineLoadable, std::string> >::iterator outer_i; + std::vector<std::pair<CdlDefineLoadable, std::string> >::iterator inner_i; + for (outer_i = headers.begin(); outer_i != headers.end(); outer_i++) { + if ("" == outer_i->second) { + continue; + } + std::string target_name = outer_i->second; + std::string header_name = directory + "/__libcdl_file2"; + Tcl_Channel header_h = Tcl_OpenFileChannel(interp->get_tcl_interpreter(), + const_cast<char*>(header_name.c_str()), "w", 0666); + if (0 == header_h) { + throw CdlInputOutputException("Unable to open file " + header_name + "\n" + + interp->get_result()); + } + // The channel may be used in several different interpreters, so + // do an extra register operation + Tcl_RegisterChannel(0, header_h); + + try { + // Output the banner. This requires an all-upper-case version of the + // header name. + std::string upper_case; + for (unsigned int i = 0; i < target_name.size(); i++) { + if (islower(target_name[i])) { + upper_case += toupper(target_name[i]); + } else if ('.' == target_name[i]) { + upper_case += '_'; + } else { + upper_case += target_name[i]; + } + } + sprintf(local_buf, banner_format, upper_case.c_str(), upper_case.c_str(), target_name.c_str()); + Tcl_Write(header_h, local_buf, -1); + + // Now iterate over all the loadables looking for ones which + // should generate #define's for this header, and invoke the + // appropriate member function. + for (inner_i = outer_i; inner_i != headers.end(); inner_i++) { + if (inner_i->second == target_name) { + inner_i->first->generate_config_header(header_h, system_h); + inner_i->second = ""; + } + } + + // The header file has now been updated. Close it and decide whether + // or not to replace the old version + Tcl_Write(header_h, "\n#endif\n", -1); + } catch(...) { + Tcl_UnregisterChannel(0, header_h); + throw; + } + Tcl_UnregisterChannel(0, header_h); + compare_and_copy(interp, header_name, directory + "/" + target_name); + } + + Tcl_Write(system_h, "\n#endif\n", -1); + } catch(...) { + Tcl_UnregisterChannel(0, system_h); + throw; + } + + // This call to UnregisterChannel automatically closes the + // channel, there is no need for an explicit Tcl_Close() call. + Tcl_UnregisterChannel(0, system_h); +#ifdef _WIN32 + Tcl_UnregisterChannel(0, stderr_chan); + Tcl_UnregisterChannel(0, stdout_chan); + Tcl_UnregisterChannel(0, stdin_chan); +#endif + compare_and_copy(interp, system_h_name, directory +"/system.h"); +} + +//}}} +//{{{ CdlToplevel::get_config_headers() + +// ---------------------------------------------------------------------------- +// Return details of the header files that should be generated. This +// allows higher-level code to detect files that should no longer +// be present, amongst other uses. +// +// The main complication is that some packages may wish to share the +// same header file, especially hardware packages. + +void +CdlToplevelBody::get_config_headers(std::vector<std::string>& headers) +{ + CYG_REPORT_FUNCNAME("CdlToplevelBody::get_config_headers"); + CYG_REPORT_FUNCARG2XV(this, &headers); + CYG_PRECONDITION_THISC(); + + // Allow the vector argument to be re-used in multiple calls. + // Strictly speaking this is better done at the application + // level, but the behaviour is consistent with get_build_info(); + headers.clear(); + + // There will always be a system.h header file with details + // of the loadables. + // FIXME: the name of this file should probably be controllable + headers.push_back("system.h"); + + // Now check each loadable and adds its header file, assuming + // this is unique. + const std::vector<CdlLoadable>& loadables = get_loadables(); + std::vector<CdlLoadable>::const_iterator i; + for (i = loadables.begin(); i != loadables.end(); i++) { + CdlDefineLoadable current = dynamic_cast<CdlDefineLoadable>(*i); + if (0 != current) { + std::string its_file = current->get_config_header(); + CYG_LOOP_INVARIANTC("" != its_file); + if (std::find(headers.begin(), headers.end(), its_file) == headers.end()) { + headers.push_back(its_file); + } + } + } + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ CdlToplevel::generate_build_tree() + +void +CdlToplevelBody::generate_build_tree(std::string build_tree, std::string install_tree) + throw(CdlInputOutputException, std::bad_alloc) +{ +} + +//}}} + +//}}}
