source: SVN/cambria/redboot/host/libcdl/build.cxx @ 1

Last change on this file since 1 was 1, checked in by Tim Harvey, 2 years ago

restored latest version of files from server backup

Signed-off-by: Tim Harvey <tharvey@…>

File size: 80.2 KB
Line 
1//{{{  Banner                           
2
3//==========================================================================
4//
5//      build.cxx
6//
7//      libcdl support for building and for header file generation
8//
9//==========================================================================
10//####COPYRIGHTBEGIN####
11//                                                                         
12// ----------------------------------------------------------------------------
13// Copyright (C) 2002, 2003 Bart Veer
14// Copyright (C) 1999, 2000, 2001 Red Hat, Inc.
15//
16// This file is part of the eCos host tools.
17//
18// This program is free software; you can redistribute it and/or modify it
19// under the terms of the GNU General Public License as published by the Free
20// Software Foundation; either version 2 of the License, or (at your option)
21// any later version.
22//
23// This program is distributed in the hope that it will be useful, but WITHOUT
24// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
25// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
26// more details.
27//
28// You should have received a copy of the GNU General Public License along with
29// this program; if not, write to the Free Software Foundation, Inc.,
30// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
31//
32// ----------------------------------------------------------------------------
33//                                                                         
34//####COPYRIGHTEND####
35//==========================================================================
36//#####DESCRIPTIONBEGIN####                                             
37//
38// Author(s):           bartv
39// Contributors:        bartv
40// Date:                1999-06-018
41//
42//####DESCRIPTIONEND####
43//==========================================================================
44
45//}}}
46//{{{  #include's                       
47
48// ----------------------------------------------------------------------------
49#include "cdlconfig.h"
50
51// Get the infrastructure types, assertions, tracing and similar
52// facilities.
53#include <cyg/infra/cyg_ass.h>
54#include <cyg/infra/cyg_trac.h>
55
56// <cdl.hxx> defines everything implemented in this module.
57// It implicitly supplies <string>, <vector> and <map> because
58// the class definitions rely on these headers.
59#include <cdlcore.hxx>
60#include <string.h>
61//}}}
62
63//{{{  Statics                         
64
65// ----------------------------------------------------------------------------
66CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlBuildLoadableBody);
67CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlBuildableBody);
68CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlDefineLoadableBody);
69CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlDefinableBody);
70
71//}}}
72//{{{  CdlBuildableBody                 
73
74//{{{  Basics                           
75
76// ----------------------------------------------------------------------------
77// There is little data specific to a buildable. The only distinguishing
78// feature is the set of properties that are supported, plus a handful
79// of functions to extract that information.
80
81CdlBuildableBody::CdlBuildableBody()
82{
83    CYG_REPORT_FUNCNAME("CdlBuildable:: default constructor");
84    CYG_REPORT_FUNCARG1XV(this);
85
86    // There is no data to initialize yet
87    cdlbuildablebody_cookie = CdlBuildableBody_Magic;
88    CYGDBG_MEMLEAK_CONSTRUCTOR();
89   
90    CYG_POSTCONDITION_THISC();
91    CYG_REPORT_RETURN();
92}
93
94CdlBuildableBody::~CdlBuildableBody()
95{
96    CYG_REPORT_FUNCNAME("CdlBuildable:: destructor");
97    CYG_REPORT_FUNCARG1XV(this);
98    CYG_PRECONDITION_THISC();
99
100    cdlbuildablebody_cookie = CdlBuildableBody_Invalid;
101    CYGDBG_MEMLEAK_DESTRUCTOR();
102   
103    CYG_REPORT_RETURN();
104}
105
106// ----------------------------------------------------------------------------
107
108std::string
109CdlBuildableBody::get_class_name() const
110{
111    CYG_REPORT_FUNCNAME("CdlBuildable::get_class_name");
112    CYG_PRECONDITION_THISC();
113    CYG_REPORT_RETURN();
114    return "buildable";
115}
116
117// ----------------------------------------------------------------------------
118
119bool
120CdlBuildableBody::check_this(cyg_assert_class_zeal zeal) const
121{
122    if (CdlBuildableBody_Magic != cdlbuildablebody_cookie) {
123        return false;
124    }
125    CYGDBG_MEMLEAK_CHECKTHIS();
126    return CdlNodeBody::check_this(zeal);
127}
128
129//}}}
130//{{{  Add and check property parsers   
131
132// ----------------------------------------------------------------------------
133
134void
135CdlBuildableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
136{
137    CYG_REPORT_FUNCNAME("CdlBuildable::add_property_parsers");
138
139    static CdlInterpreterCommandEntry commands[] =
140    {
141        CdlInterpreterCommandEntry("compile",            &CdlBuildableBody::parse_compile    ),
142        CdlInterpreterCommandEntry("object",             &CdlBuildableBody::parse_object     ),
143        CdlInterpreterCommandEntry("make_object",        &CdlBuildableBody::parse_make_object),
144        CdlInterpreterCommandEntry("make",               &CdlBuildableBody::parse_make       ),
145        CdlInterpreterCommandEntry("build_proc",         &CdlBuildableBody::parse_build_proc ),
146        CdlInterpreterCommandEntry("",                   0                                   ),
147    };
148
149    for (int i = 0; commands[i].command != 0; i++) {
150        std::vector<CdlInterpreterCommandEntry>::const_iterator j;
151        for (j = parsers.begin(); j != parsers.end(); j++) {
152            if (commands[i].name == j->name) {
153                if (commands[i].command != j->command) {
154                    CYG_FAIL("Property names are being re-used");
155                }
156                break;
157            }
158        }
159        if (j == parsers.end()) {
160            parsers.push_back(commands[i]);
161        }
162    }
163    CdlNodeBody::add_property_parsers(parsers);
164   
165    CYG_REPORT_RETURN();
166}
167
168void
169CdlBuildableBody::check_properties(CdlInterpreter interp)
170{
171    CYG_REPORT_FUNCNAME("CdlBuildable::check_properties");
172    CYG_REPORT_FUNCARG2XV(this, interp);
173    CYG_PRECONDITION_THISC();
174    CYG_PRECONDITION_CLASSC(interp);
175
176    // There are no real constraints on the number of compile
177    // properties etc.
178    // TODO: check that the relevant sources files exist,
179    //       unless marked appropriately (build_proc can create
180    //       new source files).
181   
182    CdlNodeBody::check_properties(interp);
183   
184    CYG_REPORT_RETURN();
185}
186
187//}}}
188//{{{  Property parsers                 
189
190// ----------------------------------------------------------------------------
191// Syntax: compile <file1 file2 ...>
192//
193// There are a couple of checks that could be performed here:
194//
195// 1) does each listed file actually exist? Unfortunately that approach
196//    falls foul of build_proc, which is allowed to generate source files
197//    on the fly.
198//
199// 2) does the file have a recognised suffix such as .c or .cxx. This
200//    relies libcdl having some way of knowing how to treat different
201//    files.
202//
203// For now there are no validity checks.
204//
205// A future extension may allow dependencies to be listed, as an
206// option. This would allow component vendors to specify that
207// particular custom build steps should happen before particular
208// compilations, a more robust approach than the current priority
209// scheme.
210
211int
212CdlBuildableBody::parse_compile(CdlInterpreter interp, int argc, const char* argv[])
213{
214    CYG_REPORT_FUNCNAMETYPE("parse_compile", "result %d");
215    static char* options[] = {
216        "library:",
217        0
218    };
219
220    int result = CdlParse::parse_stringvector_property(interp, argc, argv, CdlPropertyId_Compile, options, 0, true);
221   
222    CYG_REPORT_RETVAL(result);
223    return result;
224}
225
226// ----------------------------------------------------------------------------
227// A utility to break a custom build step down into its three components.
228//
229// A custom build step takes the form:
230//     target : deps
231//         rules
232//
233// This utility function takes a single string of this form and breaks
234// it down into its constituent parts.
235//
236// NOTE: this will need lots of extra code in future to allow for
237// escaped characters, spaces in filenames, etc. For now just keep
238// things simple.
239
240bool
241CdlBuildableBody::split_custom_build_step(std::string str_data, std::string& target, std::string& deps, std::string& rules,
242                                          std::string& error_msg)
243{
244    CYG_REPORT_FUNCNAMETYPE("CdlBuildable::split_custom_build_step", "result %d");
245
246    target      = "";
247    deps        = "";
248    rules       = "";
249    error_msg   = "";
250
251    const char* data  = str_data.c_str();
252
253    // Skip any leading white space, and make sure that this leaves some real data.
254    while (('\0' != *data) && isspace(*data)) {
255        data++;
256    }
257    if ('\0' == *data) {
258        error_msg = "no data in custom build_step";
259        CYG_REPORT_RETVAL(false);
260        return false;
261    }
262
263    // Now extract the target. This consists of any sequence of characters
264    // upto space, tab, colon.
265    for ( ; ('\0' != *data) && (':' != *data) && (' ' != *data) && ('\t' != *data); data++) {
266        target += *data;
267    }
268    // Discard any spaces or tabs, they are of no interest
269    while ((' ' == *data) || ('\t' == *data)) {
270        data++;
271    }
272    // The current character should be a colon
273    if (':' != *data) {
274        error_msg = "expecting a colon `;' after the target `" + target + "'";
275        CYG_REPORT_RETVAL(false);
276        return false;
277    }
278
279    // Move past the colon, and skip any further spaces or tabs
280    data++;
281    while (('\0' != *data) && ((' ' == *data) || ('\t' == *data))) {
282        data++;
283    }
284
285    // Everything from here until the end of line should be part of the deps field,
286    // including white space.
287    while (('\0' != *data) && ('\n' != *data) && (';' != *data)) {
288        deps += *data++;
289    }
290
291    if ("" == deps) {
292        error_msg = "expecting dependency list after `" + target + ":'";
293        CYG_REPORT_RETVAL(false);
294        return false;
295    }
296
297    // Having some rules is compulsory.
298    if ('\0' == *data) {
299        error_msg = "expecting one or more rules after the dependency list";
300        CYG_REPORT_RETVAL(false);
301        return false;
302    } else {
303        // We are currently at \n or ;, move on to the actual rules
304        data++;
305    }
306
307    // Rules consist of one or more lines. Any leading white space on a given
308    // line should be discarded.
309    while ('\0' != *data) {
310        // Processing the current rule. Skip leading spaces and tabs
311        while ((' ' == *data) || ('\t' == *data)) {
312            data++;
313        }
314        // Now add everything up to the next newline or EOD to the rules.
315        while (('\0' != *data) && ('\n' != *data)) {
316            rules += *data++;
317        }
318
319        // Terminate this line of the rules with a newline, even if that
320        // character is absent from the raw data.
321        rules += '\n';
322
323        // And ignore the newline in the raw data itself
324        if ('\n' == *data) {
325            data++;
326        }
327    }
328
329    // Better make sure that there are some rules. All of the looping above
330    // may just have left white space
331    if ("" == rules) {
332        error_msg = "no rules provided";
333        CYG_REPORT_RETVAL(false);
334        return false;
335    }
336
337    // Everything is ok.
338 
339    CYG_REPORT_RETVAL(true);
340    return true;
341}
342
343
344// ----------------------------------------------------------------------------
345// syntax: make <target> <rules>
346//
347// There is rather a lot of checking to be done.
348//
349// 1) the priority should be valid. In particular it should be a number
350//    within a reasonable range.
351//
352// 2) the rules should take the form:
353//    <target> : <deps> ;|\n rules
354//
355//    Where the target should be a single file, identical to the
356//    first property argument.
357
358static void
359parse_make_final_check(CdlInterpreter interp, CdlProperty_String prop)
360{
361    CYG_REPORT_FUNCNAME("parse_make_final_check");
362    CYG_PRECONDITION_CLASSC(interp);
363    CYG_PRECONDITION_CLASSC(prop);
364
365    std::string prio_string = prop->get_option("priority");
366    if ("" != prio_string) {
367        cdl_int tmp = 1;
368        if (!Cdl::string_to_integer(prio_string, tmp)) {
369            CdlParse::report_property_parse_error(interp, prop,
370                                                  "Invalid priority option, priorities should be simple numbers.");
371        } else {
372            if ((tmp < 1) || (tmp > 1024)) {
373                CdlParse::report_property_parse_error(interp, prop,
374                                                      "Invalid priority value, priorities should be in the range 1 to 1024.");
375            }
376        }
377    }
378   
379    std::string data    = prop->get_string();
380    std::string target;
381    std::string deps;
382    std::string rules;
383    std::string error_msg;
384
385    if (!CdlBuildableBody::split_custom_build_step(data, target, deps, rules, error_msg)) {
386        CdlParse::report_property_parse_error(interp, prop, "Invalid custom build step, " + error_msg);
387    }
388}
389
390int
391CdlBuildableBody::parse_make(CdlInterpreter interp, int argc, const char* argv[])
392{
393    CYG_REPORT_FUNCNAMETYPE("parse_make", "result %d");
394    static char* options[] = {
395        "priority:",
396        0
397    };
398
399    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Make, options, &parse_make_final_check);
400   
401    CYG_REPORT_RETVAL(result);
402    return result;
403}
404
405
406// ----------------------------------------------------------------------------
407// syntax: make_object <target> <rules>
408//
409// The rules here are much the same as for the "make" property.
410
411static void
412parse_make_object_final_check(CdlInterpreter interp, CdlProperty_String prop)
413{
414    CYG_REPORT_FUNCNAME("parse_make_object_final_check");
415    CYG_PRECONDITION_CLASSC(interp);
416    CYG_PRECONDITION_CLASSC(prop);
417
418
419    std::string prio_string = prop->get_option("priority");
420    if ("" != prio_string) {
421        cdl_int tmp = 1;
422        if (!Cdl::string_to_integer(prio_string, tmp)) {
423            CdlParse::report_property_parse_error(interp, prop,
424                                                  "Invalid priority option, priorities should be simple numbers.");
425        } else {
426            if ((tmp < 1) || (tmp > 1024)) {
427                CdlParse::report_property_parse_error(interp, prop,
428                                                      "Invalid priority value, priorities should be in the range 1 to 1024.");
429            }
430        }
431    }
432   
433    std::string data    = prop->get_string();
434    std::string target;
435    std::string deps;
436    std::string rules;
437    std::string error_msg;
438
439    if (!CdlBuildableBody::split_custom_build_step(data, target, deps, rules, error_msg)) {
440        CdlParse::report_property_parse_error(interp, prop, "Invalid custom build step, " + error_msg);
441    }
442}
443
444int
445CdlBuildableBody::parse_make_object(CdlInterpreter interp, int argc, const char* argv[])
446{
447    CYG_REPORT_FUNCNAMETYPE("parse_make_object", "result %d");
448    static char* options[] = {
449        "library:",
450        "priority:",
451        0
452    };
453
454    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_MakeObject, options,
455                                                 &parse_make_object_final_check);
456   
457    CYG_REPORT_RETVAL(result);
458    return result;
459}
460
461
462// ----------------------------------------------------------------------------
463// Syntax: object <file1> <file2> ...
464
465int
466CdlBuildableBody::parse_object(CdlInterpreter interp, int argc, const char* argv[])
467{
468    CYG_REPORT_FUNCNAMETYPE("parse_object", "result %d");
469    static char* options[] = {
470        "library:",
471        0
472    };
473
474    int result = CdlParse::parse_stringvector_property(interp, argc, argv, CdlPropertyId_Object, options, 0, true);
475   
476    CYG_REPORT_RETVAL(result);
477    return result;
478}
479
480// ----------------------------------------------------------------------------
481// Syntax: build_proc { tcl code }
482
483int
484CdlBuildableBody::parse_build_proc(CdlInterpreter interp, int argc, const char* argv[])
485{
486    CYG_REPORT_FUNCNAMETYPE("parse_build_proc", "result %d");
487
488    int result = CdlParse::parse_tclcode_property(interp, argc, argv, CdlPropertyId_BuildProc, 0, 0);
489
490    CYG_REPORT_RETVAL(result);
491    return result;
492}
493
494//}}}
495//{{{  update_build_info()             
496
497// ----------------------------------------------------------------------------
498// Most of the work is done in update_all_build_info(). The update_build_info()
499// merely checks the active and enabled state first.
500void
501CdlBuildableBody::update_build_info(CdlBuildInfo_Loadable& build_info, std::string library) const
502{
503    CYG_REPORT_FUNCNAME("CdlBuildable::update_build_info");
504    CYG_REPORT_FUNCARG2XV(this, &build_info);
505    CYG_PRECONDITION_THISC();
506    CYG_PRECONDITIONC("" != library);
507
508    if (!is_active()) {
509        CYG_REPORT_RETURN();
510        return;
511    }
512
513    CdlConstValuable valuable = dynamic_cast<CdlConstValuable>(this);
514    if (0 != valuable) {
515        if (!valuable->is_enabled()) {
516            CYG_REPORT_RETURN();
517            return;
518        }
519    }
520
521    update_all_build_info(build_info, library);
522   
523    CYG_REPORT_RETURN();
524}
525
526//}}}
527//{{{  update_all_build_info()         
528
529// ----------------------------------------------------------------------------
530// There are four properties to be considered, each of which may occur
531// multiple times: "compile", "object", "make_object", and "make".
532// Each of these will result in separate additions to the build_info
533// structure.
534
535void
536CdlBuildableBody::update_all_build_info(CdlBuildInfo_Loadable& build_info, std::string package_library) const
537{
538    CYG_REPORT_FUNCNAME("CdlBuildable::update_all_build_info");
539    CYG_REPORT_FUNCARG2XV(this, &build_info);
540    CYG_PRECONDITION_THISC();
541    CYG_PRECONDITIONC("" != package_library);
542
543    // Get some information about the owning loadable first.
544    CdlLoadable loadable        = get_owner();
545    CYG_ASSERT_CLASSC(loadable);
546    std::string directory       = loadable->get_directory();
547    std::string repository      = loadable->get_repository();
548    CYG_ASSERTC("" != directory);
549    CYG_ASSERTC("" != repository);
550    CdlInterpreter interp       = loadable->get_interpreter();
551    CYG_ASSERT_CLASSC(interp);
552
553    // The interpreter needs some information about the locations
554    // of various things. This code has to be kept in step with
555    // CdlLoadable::find_relative_file()
556    interp->set_variable("::cdl_topdir", repository);
557    interp->set_variable("::cdl_pkgdir", directory);
558
559    // For many packages the sources will reside in a src subdirectory.
560    // For simple packages the sources may live directly at the toplevel
561    bool has_src_subdir    = loadable->has_subdirectory("src");
562
563    // NOTE: the object property is not yet supported
564    std::vector<CdlProperty> compile_properties;
565    get_properties(CdlPropertyId_Compile, compile_properties);
566    std::vector<CdlProperty> makeobject_properties;
567    get_properties(CdlPropertyId_MakeObject, makeobject_properties);
568    std::vector<CdlProperty> make_properties;
569    get_properties(CdlPropertyId_Make, make_properties);
570    std::vector<CdlProperty>::const_iterator prop_i;
571
572    for (prop_i = compile_properties.begin(); prop_i != compile_properties.end(); prop_i++) {
573        CdlProperty_StringVector compile_prop = dynamic_cast<CdlProperty_StringVector>(*prop_i);
574        CYG_LOOP_INVARIANT_CLASSC(compile_prop);
575
576        // Does this property have a library option?
577        std::string current_library = compile_prop->get_option("library");
578        if ("" == current_library) {
579            current_library = package_library;
580        }
581
582        const std::vector<std::string>& files   = compile_prop->get_strings();
583        std::vector<std::string>::const_iterator file_i;
584
585        for (file_i = files.begin(); file_i != files.end(); file_i++) {
586
587            // For each listed file, try to find it. If this is unsuccessful
588            // then assume that the file will be generated later on.
589            std::string path = loadable->find_relative_file(*file_i, "src");
590            if ("" == path) {
591                if (has_src_subdir) {
592                    path = "src/" + *file_i;
593                } else {
594                    path = *file_i;
595                }
596            }
597
598            // Now check whether or not the specified file is already present.
599            std::vector<CdlBuildInfo_Compile>::const_iterator info_i;
600            for (info_i = build_info.compiles.begin(); info_i != build_info.compiles.end(); info_i++) {
601                if ((current_library == info_i->library) && (path == info_i->source)) {
602                    break;
603                }
604            }
605            if (info_i == build_info.compiles.end()) {
606                CdlBuildInfo_Compile new_info;
607                new_info.library    = current_library;
608                new_info.source     = path;
609                build_info.compiles.push_back(new_info);
610            }
611        }
612    }
613
614    for (prop_i = makeobject_properties.begin(); prop_i != makeobject_properties.end(); prop_i++) {
615        CdlProperty_String prop = dynamic_cast<CdlProperty_String>(*prop_i);
616        CYG_LOOP_INVARIANT_CLASSC(prop);
617
618        // Does thie property have a library option?
619        std::string current_library = prop->get_option("library");
620        if ("" == current_library) {
621            current_library = package_library;
622        }
623
624        // How about a priority field? The default priority for make_object is 100
625        // We can rely on the validation done during the parsing process
626        cdl_int priority = 100;
627        std::string priority_option = prop->get_option("priority");
628        if ("" != priority_option) {
629            Cdl::string_to_integer(priority_option, priority);
630        }
631
632        // What we need now is the separate target, deps, and rules. These
633        // can be obtained via a utility. The raw data will have been validated
634        // already.
635        std::string raw_data = prop->get_string();
636        std::string target;
637        std::string deps;
638        std::string rules;
639        std::string error_msg;
640        bool result;
641
642        result = CdlBuildableBody::split_custom_build_step(raw_data, target, deps, rules, error_msg);
643        CYG_ASSERTC(true == result);
644
645        // Construct a local object, then copy it into the vector
646        CdlBuildInfo_MakeObject local_copy;
647        local_copy.priority     = priority;
648        local_copy.library      = current_library;
649        local_copy.object       = target;
650        local_copy.deps         = deps;
651        local_copy.rules        = rules;
652
653        build_info.make_objects.push_back(local_copy);
654    }
655   
656    for (prop_i = make_properties.begin(); prop_i != make_properties.end(); prop_i++) {
657        CdlProperty_String prop = dynamic_cast<CdlProperty_String>(*prop_i);
658        CYG_LOOP_INVARIANT_CLASSC(prop);
659
660        // Is there a priority field? The default priority for make is
661        // 300 We can rely on the validation done during the parsing
662        // process
663        cdl_int priority = 300;
664        std::string priority_option = prop->get_option("priority");
665        if ("" != priority_option) {
666            Cdl::string_to_integer(priority_option, priority);
667        }
668
669        // What we need now is the separate target, deps, and rules. These
670        // can be obtained via a utility. The raw data will have been validated
671        // already.
672        std::string raw_data = prop->get_string();
673        std::string target;
674        std::string deps;
675        std::string rules;
676        std::string error_msg;
677        bool result;
678
679        result = CdlBuildableBody::split_custom_build_step(raw_data, target, deps, rules, error_msg);
680        CYG_ASSERTC(true == result);
681
682        // Construct a local object, then copy it into the vector
683        CdlBuildInfo_Make local_copy;
684        local_copy.priority     = priority;
685        local_copy.target       = target;
686        local_copy.deps         = deps;
687        local_copy.rules        = rules;
688
689        build_info.makes.push_back(local_copy);
690    }
691   
692    CYG_REPORT_RETURN();
693}
694
695//}}}
696
697//}}}
698//{{{  CdlBuildLoadableBody             
699
700//{{{  Class variables                         
701
702// ----------------------------------------------------------------------------
703// This variable controls the default library that should be generated.
704// Some applications may wish to override this.
705char* CdlBuildLoadableBody::default_library_name        = "libtarget.a";
706
707// The pattern that should be used to identify header files.
708// FIXME: this information should come out of a data file
709char* CdlBuildLoadableBody::default_headers_glob_pattern = "*.h *.hxx *.inl *.si *.inc";
710
711//}}}
712//{{{  The simple stuff                         
713
714// ----------------------------------------------------------------------------
715
716CdlBuildLoadableBody::CdlBuildLoadableBody()
717    : CdlLoadableBody()
718{
719    CYG_REPORT_FUNCNAME("CdlBuildLoadable:: default constructor");
720    CYG_REPORT_FUNCARG1XV(this);
721
722    // There is no data to initialize
723    cdlbuildloadablebody_cookie = CdlBuildLoadableBody_Magic;
724    CYGDBG_MEMLEAK_CONSTRUCTOR();
725   
726    CYG_POSTCONDITION_THISC();
727    CYG_REPORT_RETURN();
728}
729
730CdlBuildLoadableBody::~CdlBuildLoadableBody()
731{
732    CYG_REPORT_FUNCNAME("CdlBuildLoadable:: destructor");
733    CYG_REPORT_FUNCARG1XV(this);
734    CYG_PRECONDITION_THISC();
735
736    cdlbuildloadablebody_cookie = CdlBuildLoadableBody_Invalid;
737    CYGDBG_MEMLEAK_DESTRUCTOR();
738   
739    CYG_REPORT_RETURN();
740}
741
742// ----------------------------------------------------------------------------
743
744std::string
745CdlBuildLoadableBody::get_class_name() const
746{
747    CYG_REPORT_FUNCNAME("CdlBuildLoadable::get_class_name");
748    CYG_PRECONDITION_THISC();
749    CYG_REPORT_RETURN();
750    return "build_loadable";
751}
752
753// ----------------------------------------------------------------------------
754
755bool
756CdlBuildLoadableBody::check_this(cyg_assert_class_zeal zeal) const
757{
758    if (CdlBuildLoadableBody_Magic != cdlbuildloadablebody_cookie) {
759        return false;
760    }
761    CYGDBG_MEMLEAK_CHECKTHIS();
762    return CdlContainerBody::check_this(zeal) && CdlNodeBody::check_this(zeal);
763}
764
765//}}}
766//{{{  Property parsers                         
767
768// ----------------------------------------------------------------------------
769
770void
771CdlBuildLoadableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
772{
773    CYG_REPORT_FUNCNAME("CdlBuildLoadable::add_property_parsers");
774
775    static CdlInterpreterCommandEntry commands[] =
776    {
777        CdlInterpreterCommandEntry("library",            &CdlBuildLoadableBody::parse_library       ),
778        CdlInterpreterCommandEntry("makefile",           &CdlBuildLoadableBody::parse_makefile      ),
779        CdlInterpreterCommandEntry("include_dir",        &CdlBuildLoadableBody::parse_include_dir   ),
780        CdlInterpreterCommandEntry("include_files",      &CdlBuildLoadableBody::parse_include_files ),
781        CdlInterpreterCommandEntry("",                   0                                          )
782    };
783
784    for (int i = 0; commands[i].command != 0; i++) {
785        std::vector<CdlInterpreterCommandEntry>::const_iterator j;
786        for (j = parsers.begin(); j != parsers.end(); j++) {
787            if (commands[i].name == j->name) {
788                if (commands[i].command != j->command) {
789                    CYG_FAIL("Property names are being re-used");
790                }
791                break;
792            }
793        }
794        if (j == parsers.end()) {
795            parsers.push_back(commands[i]);
796        }
797    }
798   
799    CYG_REPORT_RETURN();
800}
801
802void
803CdlBuildLoadableBody::check_properties(CdlInterpreter interp)
804{
805    CYG_REPORT_FUNCNAME("CdlBuildLoadable::check_properties");
806    CYG_REPORT_FUNCARG2XV(this, interp);
807    CYG_PRECONDITION_THISC();
808    CYG_PRECONDITION_CLASSC(interp);
809
810    CdlNodeBody::check_properties(interp);
811   
812    CYG_REPORT_RETURN();
813}
814
815// ----------------------------------------------------------------------------
816// syntax: library <filename>
817//
818// NOTE: there should probably be a check that the library name is in
819// a valid format, i.e. libxxx.a
820
821int
822CdlBuildLoadableBody::parse_library(CdlInterpreter interp, int argc, const char* argv[])
823{
824    CYG_REPORT_FUNCNAMETYPE("parse_library", "result %d");
825
826    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Library, 0, 0);
827   
828    CYG_REPORT_RETVAL(result);
829    return result;
830}
831
832// ----------------------------------------------------------------------------
833// syntax: makefile <filename>
834//
835// NOTE: possibly there should be a check that the makefile exists.
836// Do we want to allow build_proc's to generate makefiles?
837int
838CdlBuildLoadableBody::parse_makefile(CdlInterpreter interp, int argc, const char* argv[])
839{
840    CYG_REPORT_FUNCNAMETYPE("parse_makefile", "result %d");
841
842    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Makefile, 0, 0);
843   
844    CYG_REPORT_RETVAL(result);
845    return result;
846}
847
848// ----------------------------------------------------------------------------
849// syntax: include_dir <directory name>
850int
851CdlBuildLoadableBody::parse_include_dir(CdlInterpreter interp, int argc, const char* argv[])
852{
853    CYG_REPORT_FUNCNAMETYPE("parse_include_dir", "result %d");
854
855    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_IncludeDir, 0, 0);
856   
857    CYG_REPORT_RETVAL(result);
858    return result;
859}
860
861// ----------------------------------------------------------------------------
862// Syntax: include_files <file1 file2 ...>
863//
864// This lists the header files that should be copied into the install tree
865// as part of the build operation. In the absence of an include_files property
866// there should be an include subdirectory, and all files in that subdirectory
867// are assumed to be exportable headers.
868//
869// NOTE: add a finalizer to check that the files exist or get created.
870
871int
872CdlBuildLoadableBody::parse_include_files(CdlInterpreter interp, int argc, const char* argv[])
873{
874    CYG_REPORT_FUNCNAMETYPE("parse_include_files", "result %d");
875
876    int result = CdlParse::parse_stringvector_property(interp, argc, argv, CdlPropertyId_IncludeFiles, 0, 0, true);
877   
878    CYG_REPORT_RETVAL(result);
879    return result;
880}
881
882//}}}
883//{{{  update_build_info()                     
884
885// ----------------------------------------------------------------------------
886// This utility routine takes care of filling in a Buildinfo_Loadable
887// structure with the appropriate header file information. This involves
888// the following:
889//
890// 1) there may be an include_dir property. This affects the destination
891//     of any header files that are listed.
892//
893// 2) the loadable may or may not have an include subdirectory. For
894//    non-trivial packages the include subdirectory provides a clean
895//    way of separating interface and implementation. For simple
896//    packages it is too heavyweight.
897//
898// 3) there may be one or more include_files property. If so then these
899//    specify all the files that should be exported.
900//
901// 4) otherwise if there is an include subdirectory then we need to
902//    know all the header files in that subdirectory. A Tcl script is
903//    used for this.
904//
905// 5) otherwise all the header files below the package directory itself
906//    are of interest.
907
908static void
909update_header_file_info(CdlConstBuildLoadable loadable, CdlBuildInfo_Loadable& build_info)
910{
911    CYG_REPORT_FUNCNAME("update_header_file_info");
912    CYG_REPORT_FUNCARG2XV(loadable, &build_info);
913    CYG_PRECONDITION_CLASSC(loadable);
914
915    std::string dest_dir = "";
916    CdlProperty include_dir_prop = loadable->get_property(CdlPropertyId_IncludeDir);
917    if (0 != include_dir_prop) {
918        CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(include_dir_prop);
919        CYG_ASSERT_CLASSC(strprop);
920        dest_dir = strprop->get_string();
921    }
922
923    bool has_include_subdir = loadable->has_subdirectory("include");
924
925    std::vector<CdlProperty> include_file_properties;
926    loadable->get_properties(CdlPropertyId_IncludeFiles, include_file_properties);
927    if (include_file_properties.size() > 0) {
928        std::vector<CdlProperty>::const_iterator prop_i;
929        for (prop_i = include_file_properties.begin(); prop_i != include_file_properties.end(); prop_i++) {
930           
931            CdlProperty_StringVector strvprop = dynamic_cast<CdlProperty_StringVector>(*prop_i);
932            CYG_ASSERT_CLASSC(strvprop);
933
934            const std::vector<std::string>& filenames = strvprop->get_strings();
935            std::vector<std::string>::const_iterator file_i;
936            for (file_i = filenames.begin(); file_i != filenames.end(); file_i++) {
937                std::string path = loadable->find_relative_file(*file_i, "include");
938                // Assume that the header file will be generated by a build_proc
939                if ("" == path) {
940                    if (has_include_subdir) {
941                        path = "include/" + *file_i;
942                    } else {
943                        path = *file_i;
944                    }
945                }
946                CdlBuildInfo_Header local_copy;
947                local_copy.source       = path;
948                local_copy.destination  = "";
949                if ("" != dest_dir) {
950                    local_copy.destination = dest_dir + "/";
951                }
952                // At this stage "path" may begin with "include/", which should not
953                // be present in the destination.
954                const char* tmp = path.c_str();
955                if (0 == strncmp("include/", tmp, 8)) {
956                    local_copy.destination += &(tmp[8]);
957                } else {
958                    local_copy.destination += path;
959                }
960                build_info.headers.push_back(local_copy);
961            }
962        }
963        CYG_REPORT_RETURN();
964        return;
965    }
966
967    // It is necessary to search for the appropriate files.
968    CdlInterpreter interp = loadable->get_interpreter();
969    std::string    path   = loadable->get_repository() + "/" + loadable->get_directory();
970    if (has_include_subdir) {
971        std::vector<std::string> files;
972        std::vector<std::string>::const_iterator file_i;
973        interp->locate_all_files(path + "/include", files);
974        for (file_i = files.begin(); file_i != files.end(); file_i++) {
975            // NOTE: for now discard any header files in the pkgconf subdirectory
976            if (0 == strncmp("pkgconf/", file_i->c_str(), 8)) {
977                continue;
978            }
979            if ('~' == *(file_i->rbegin())) {
980                continue;
981            }
982            CdlBuildInfo_Header local_copy;
983            local_copy.source   = "include/" + *file_i;
984            local_copy.destination      = "";
985            if ("" != dest_dir) {
986                local_copy.destination = dest_dir + "/";
987            }
988            local_copy.destination += *file_i;
989            build_info.headers.push_back(local_copy);
990        }
991    } else {
992        // Look for all header files, which for now means files with
993        // a .h, .hxx, .inl or .inc extension.
994        // FIXME: the definition of what constitutes a header file
995        // should not be hard-wired here.
996        std::vector<std::string> files;
997        std::vector<std::string>::const_iterator file_i;
998        interp->locate_all_files(path, files);
999        for (file_i = files.begin(); file_i != files.end(); file_i++) {
1000
1001            // Problems with libstdc++ versions, use C comparisons instead.
1002            const char* c_string = file_i->c_str();
1003            unsigned int len = strlen(c_string);
1004            if (((len >= 2) && (0 == strncmp(c_string + len - 2, ".h", 2)))        ||
1005                ((len >= 4) && (0 == strncmp(c_string + len - 4, ".hxx", 4)))      ||
1006                ((len >= 4) && (0 == strncmp(c_string + len - 4, ".inl", 4)))      ||
1007                ((len >= 4) && (0 == strncmp(c_string + len - 4, ".inc", 4)))) {
1008               
1009                CdlBuildInfo_Header local_copy;
1010                local_copy.source = *file_i;
1011                local_copy.destination = "";
1012                if ("" != dest_dir) {
1013                    local_copy.destination = dest_dir + "/";
1014                }
1015                local_copy.destination += *file_i;
1016                build_info.headers.push_back(local_copy);
1017            }
1018        }
1019    }
1020
1021    CYG_REPORT_RETURN();
1022    return;
1023}
1024
1025// ----------------------------------------------------------------------------
1026// Updating a loadable build's info involves two steps. First, there
1027// is some information associated with the loadable as a whole such as
1028// header files. Second, each buildable in the loadable (including itself)
1029// may contain properties such as compile etc. This is all handled via
1030// a CdlBuildable member function.
1031
1032void
1033CdlBuildLoadableBody::update_build_info(CdlBuildInfo& build_info) const
1034{
1035    CYG_REPORT_FUNCNAME("CdlBuildLoadable::update_build_info");
1036    CYG_REPORT_FUNCARG2XV(this, &build_info);
1037    CYG_PRECONDITION_THISC();
1038
1039    // It is not possible to disable a loadable itself: either the
1040    // loadable is present or it is not (although in some cases users
1041    // may be able to change versions). However, because of reparenting
1042    // it is possible for a loadable to be below a disabled container,
1043    // and hence it is still necessary to check whether or not the
1044    // loadable is active.
1045    if (!is_active()) {
1046        CYG_REPORT_RETURN();
1047        return;
1048    }
1049
1050    // Time to add a new CdlBuildInfo_Loadable object to the current
1051    // vector. The name and directory can be filled in straightaway,
1052    // the vectors will all be initialized to empty.
1053    CdlBuildInfo_Loadable tmp_info;
1054    build_info.entries.push_back(tmp_info);
1055    CdlBuildInfo_Loadable& this_info = *(build_info.entries.rbegin());
1056    this_info.name          = get_name();
1057    this_info.repository    = get_repository();
1058    this_info.directory     = get_directory();
1059
1060    // Take care of the header files
1061    update_header_file_info(this, this_info);
1062   
1063    // Work out the library name appropriate for this loadable.
1064    // There may be a library property, otherwise the global default
1065    // should be used.
1066    std::string loadable_library = default_library_name;
1067    if (has_property(CdlPropertyId_Library)) {
1068        CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(get_property(CdlPropertyId_Library));
1069        loadable_library = strprop->get_string();
1070    }
1071   
1072    const std::vector<CdlNode>& contents = get_owned();
1073    std::vector<CdlNode>::const_iterator node_i;
1074    for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
1075        CdlBuildable buildable = dynamic_cast<CdlBuildable>(*node_i);
1076        if (0 != buildable) {
1077            buildable->update_build_info(this_info, loadable_library);
1078        }
1079    }
1080   
1081    CYG_REPORT_RETURN();
1082}
1083
1084// This is much the same as the above, but there is no test for
1085// active either at the loadable level or for the individual buildables.
1086void
1087CdlBuildLoadableBody::update_all_build_info(CdlBuildInfo& build_info) const
1088{
1089    CYG_REPORT_FUNCNAME("CdlBuildLoadable::update_all_build_info");
1090    CYG_REPORT_FUNCARG2XV(this, &build_info);
1091    CYG_PRECONDITION_THISC();
1092
1093    CdlBuildInfo_Loadable tmp_info;
1094    build_info.entries.push_back(tmp_info);
1095    CdlBuildInfo_Loadable& this_info = *(build_info.entries.rbegin());
1096    this_info.name       = get_name();
1097    this_info.directory  = get_directory();
1098
1099    std::string loadable_library = default_library_name;
1100    if (has_property(CdlPropertyId_Library)) {
1101        CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(get_property(CdlPropertyId_Library));
1102        loadable_library = strprop->get_string();
1103    }
1104   
1105    const std::vector<CdlNode>& contents = get_owned();
1106    std::vector<CdlNode>::const_iterator node_i;
1107    for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
1108        CdlBuildable buildable = dynamic_cast<CdlBuildable>(*node_i);
1109        if (0 != buildable) {
1110            buildable->update_build_info(this_info, loadable_library);
1111        }
1112    }
1113   
1114    CYG_REPORT_RETURN();
1115    CYG_REPORT_RETURN();
1116}
1117
1118//}}}
1119
1120//}}}
1121//{{{  Version number #define's         
1122
1123// ----------------------------------------------------------------------------
1124// Given a package xxxPKG_A_B_C with a version V1_2_3, generate additional
1125// #define's of the form:
1126//
1127//   #define xxxNUM_A_B_C_VERSION_MAJOR 1
1128//   #define xxxNUM_A_B_C_VERSION_MINOR 2
1129//   #define xxxNUM_A_B_C_VERSION_RELEASE 3
1130//
1131// The goal here is to allow application code to cope with API
1132// changes (which of course should be a rare event but cannot be
1133// eliminated completely). C preprocessor #if statements are
1134// essentially limited to numerical values, so there is no easy
1135// way of coping with V1_2_3 at the preprocessor level. However it
1136// is possible to cope with VERSION_NUMBER #define's.
1137//
1138// Note that only application code and third party packages are
1139// affected.
1140//
1141// These #define's go into system.h, alongside the main definition of
1142// the package. There seems to be little point in putting them in the
1143// package's own configuration header.
1144//
1145// There are three problems. First, what should be done for packages
1146// which do not follow the naming conventions? Given a completely
1147// random package rather than something like xxxPKG_..., what symbol
1148// names should be used? Basically, if the package does not follow the
1149// naming convention then there is no safe way of generating new
1150// symbols. Any names that are chosen might clash. Of course even for
1151// packages that do follow the naming convention a clash is still
1152// possible, just a lot less likely.
1153//
1154// Conclusion: if a package does not follow the naming convention, do
1155// not generate version #define's for it.
1156//
1157// Second, what happens if a different version numbering scheme is
1158// used? For example the release number might be absent. Version
1159// numbering schemes might change between releases, but application
1160// code may still check the #define's.
1161//
1162// Third and related, what should happen for "current" and anoncvs? Do
1163// we want to look at what other versions are installed and bump one
1164// of the numbers?
1165//
1166// Conclusion: the version #define's always have to be generated,
1167// even if they are not present in the version string, to allow
1168// application code to test these symbols anyway. A safe default is
1169// necessary, and -1 is probably the best bet. For example, if
1170// the version is bumped from 1.3.287 to 1.4 then the release number
1171// for the latter is set to -1. Another possible default would be
1172// 0, but that could cause problems for packages that start counting
1173// from 0 (not a common practice, but...)
1174//
1175// This leaves the question of what to do about "current". Chances are
1176// that "current" comes from anoncvs and is always more recent than
1177// any official release, so when comparing versions "current" should
1178// always be greater than anything else. This can be achieved by using
1179// a sufficiently large number for the major version. In practice
1180// it is cleaner to have another #define to indicate the current
1181// version, and then define package versions to match, i.e.:
1182//
1183//   #define CYGNUM_VERSION_CURRENT 0x7fffff00
1184//   ...
1185//   #define xxxNUM_A_B_C_VERSION_MAJOR   CYGNUM_VERSION_CURRENT
1186//   #define xxxNUM_A_B_C_VERSION_MINOR   -1
1187//   #define xxxNUM_A_B_C_VERSION_RELEASE -1
1188//
1189// All comparisons should now work sensibly. Leaving a little bit
1190// of slack for VERSION_CURRENT seems like a good precaution.
1191
1192static void
1193system_h_add_version_header(Tcl_Channel system_h)
1194{
1195    CYG_REPORT_FUNCNAME("system_h_add_version_header");
1196    Tcl_Write(system_h, "#define CYGNUM_VERSION_CURRENT 0x7fffff00\n", -1);
1197    CYG_REPORT_RETURN();
1198}
1199
1200static void
1201system_h_add_package_versioning(Tcl_Channel system_h, std::string name, std::string value)
1202{
1203    CYG_REPORT_FUNCNAME("system_h_add_package_versioning");
1204
1205    char name_buf[256];
1206    char line_buf[512];
1207   
1208    // The first thing to check is that the package name can be used
1209    // as the basis for the version symbols.
1210    bool ok = false;
1211    unsigned int i;
1212    for (i = 0; i < name.size(); i++) {
1213        if ('_' == name[i]) {
1214            if (3 < i) {
1215                if ((name[i-3] == 'P') && (name[i-2] == 'K') && (name[i-1] == 'G')) {
1216                    ok = true;
1217                }
1218            }
1219            break;
1220        }
1221    }
1222    if (name.size() >= 256) {
1223        ok = false;
1224    }
1225    if (!ok) {
1226        CYG_REPORT_RETURN();
1227        return;
1228    }
1229
1230    strcpy(name_buf, name.c_str());
1231   
1232    // Change from xxxPKG to xxxNUM
1233    name_buf[i - 3] = 'N';
1234    name_buf[i - 2] = 'U';
1235    name_buf[i - 1] = 'M';
1236
1237    // Now determine the version strings.
1238    std::string major   = "-1";
1239    std::string minor   = "-1";
1240    std::string release = "-1";
1241    if ("current" == value) {
1242        major   = "CYGNUM_VERSION_CURRENT";
1243    } else {
1244        Cdl::split_version_string(value, major, minor, release);
1245    }
1246
1247    sprintf(line_buf, "#define %s_VERSION_MAJOR %s\n", name_buf, major.c_str());
1248    Tcl_Write(system_h, line_buf, -1);
1249    sprintf(line_buf, "#define %s_VERSION_MINOR %s\n", name_buf, minor.c_str());
1250    Tcl_Write(system_h, line_buf, -1);
1251    sprintf(line_buf, "#define %s_VERSION_RELEASE %s\n", name_buf, release.c_str());
1252    Tcl_Write(system_h, line_buf, -1);
1253   
1254    CYG_REPORT_RETURN();
1255}
1256
1257//}}}
1258//{{{  CdlDefinableBody                 
1259
1260//{{{  Basics                                           
1261
1262// ----------------------------------------------------------------------------
1263
1264CdlDefinableBody::CdlDefinableBody()
1265{
1266    CYG_REPORT_FUNCNAME("CdlDefinable:: default constructor");
1267    CYG_REPORT_FUNCARG1XV(this);
1268
1269    // There is no data to initialize
1270    cdldefinablebody_cookie = CdlDefinableBody_Magic;
1271    CYGDBG_MEMLEAK_CONSTRUCTOR();
1272   
1273    CYG_POSTCONDITION_THISC();
1274    CYG_REPORT_RETURN();
1275}
1276
1277CdlDefinableBody::~CdlDefinableBody()
1278{
1279    CYG_REPORT_FUNCNAME("CdlDefinable:: destructor");
1280    CYG_REPORT_FUNCARG1XV(this);
1281    CYG_PRECONDITION_THISC();
1282
1283    cdldefinablebody_cookie = CdlDefinableBody_Invalid;
1284    CYGDBG_MEMLEAK_DESTRUCTOR();
1285   
1286    CYG_REPORT_RETURN();
1287}
1288
1289// ----------------------------------------------------------------------------
1290
1291std::string
1292CdlDefinableBody::get_class_name() const
1293{
1294    CYG_REPORT_FUNCNAME("CdlDefinable::get_class_name");
1295    CYG_PRECONDITION_THISC();
1296    CYG_REPORT_RETURN();
1297    return "definable";
1298}
1299
1300// ----------------------------------------------------------------------------
1301
1302bool
1303CdlDefinableBody::check_this(cyg_assert_class_zeal zeal) const
1304{
1305    if (CdlDefinableBody_Magic != cdldefinablebody_cookie) {
1306        return false;
1307    }
1308    CYGDBG_MEMLEAK_CHECKTHIS();
1309    return CdlNodeBody::check_this(zeal);
1310}
1311
1312//}}}
1313//{{{  add_property_parser() and check_properties()     
1314
1315// ----------------------------------------------------------------------------
1316
1317void
1318CdlDefinableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
1319{
1320    CYG_REPORT_FUNCNAME("CdlDefinable::add_property_parsers");
1321
1322    static CdlInterpreterCommandEntry commands[] =
1323    {
1324        CdlInterpreterCommandEntry("no_define",          &parse_no_define     ),
1325        CdlInterpreterCommandEntry("define",             &parse_define        ),
1326        CdlInterpreterCommandEntry("define_format",      &parse_define_format ),
1327        CdlInterpreterCommandEntry("define_proc",        &parse_define_proc   ),
1328        CdlInterpreterCommandEntry("if_define",          &parse_if_define     ),
1329        CdlInterpreterCommandEntry("",                   0                    )
1330    };
1331
1332    for (int i = 0; commands[i].command != 0; i++) {
1333        std::vector<CdlInterpreterCommandEntry>::const_iterator j;
1334        for (j = parsers.begin(); j != parsers.end(); j++) {
1335            if (commands[i].name == j->name) {
1336                if (commands[i].command != j->command) {
1337                    CYG_FAIL("Property names are being re-used");
1338                }
1339                break;
1340            }
1341        }
1342        if (j == parsers.end()) {
1343            parsers.push_back(commands[i]);
1344        }
1345    }
1346    CdlNodeBody::add_property_parsers(parsers);
1347   
1348    CYG_REPORT_RETURN();
1349}
1350
1351void
1352CdlDefinableBody::check_properties(CdlInterpreter interp)
1353{
1354    CYG_REPORT_FUNCNAME("CdlDefinable::check_properties");
1355    CYG_REPORT_FUNCARG2XV(this, interp);
1356    CYG_PRECONDITION_THISC();
1357    CYG_PRECONDITION_CLASSC(interp);
1358
1359    // There should be at most one each of no_define and define_format.
1360    if (count_properties(CdlPropertyId_NoDefine) > 1) {
1361        CdlParse::report_error(interp, "", "There should be at most one no_define property.");
1362    }
1363    if (count_properties(CdlPropertyId_DefineFormat) > 1) {
1364        CdlParse::report_error(interp, "", "There should be at most one define_format property.");
1365    }
1366    if (has_property(CdlPropertyId_NoDefine) && has_property(CdlPropertyId_DefineFormat)) {
1367        CdlParse::report_error(interp, "", "The no_define and define_format properties are mutually exclusive.");
1368    }
1369    // FIXME: the define_format property only makes sense for certain
1370    // flavors. However the flavor property may not have been processed yet.
1371   
1372    CdlNodeBody::check_properties(interp);
1373   
1374    CYG_REPORT_RETURN();
1375}
1376
1377//}}}
1378//{{{  Definable properties                             
1379
1380// ----------------------------------------------------------------------------
1381// Syntax: no_define
1382int
1383CdlDefinableBody::parse_no_define(CdlInterpreter interp, int argc, const char* argv[])
1384{
1385    CYG_REPORT_FUNCNAMETYPE("parse_no_define", "result %d");
1386
1387    int result = CdlParse::parse_minimal_property(interp, argc, argv, CdlPropertyId_NoDefine, 0, 0);
1388   
1389    CYG_REPORT_RETVAL(result);
1390    return result;
1391}
1392
1393// ----------------------------------------------------------------------------
1394// syntax: define <symbol>
1395
1396// The argument to "define" should be a valid C preprocessor symbol.
1397static void
1398parse_define_final_check(CdlInterpreter interp, CdlProperty_String prop)
1399{
1400    CYG_REPORT_FUNCNAME("parse_define_final_check");
1401    CYG_PRECONDITION_CLASSC(prop);
1402    CYG_PRECONDITION_CLASSC(interp);
1403   
1404    const std::string& str = prop->get_string();
1405
1406    if (!Cdl::is_valid_c_preprocessor_symbol(str)) {
1407        CdlParse::report_property_parse_error(interp, prop, str + " is not a valid C preprocessor symbol");
1408    }
1409
1410    // There may be a file option. At this stage the only valid filename
1411    // that can be used here is system.h
1412    std::string file_option = prop->get_option("file");
1413    if (("" != file_option) && ("system.h" != file_option)) {
1414        CdlParse::report_property_parse_error(interp, prop, "Invalid -file option " + file_option);
1415    }
1416
1417    // FIXME: validate the format string
1418   
1419    CYG_REPORT_RETURN();
1420}
1421
1422int
1423CdlDefinableBody::parse_define(CdlInterpreter interp, int argc, const char* argv[])
1424{
1425    CYG_REPORT_FUNCNAMETYPE("parse_define", "result %d");
1426
1427    static char* options[] = {
1428        "file:",
1429        "format:",
1430        0
1431    };
1432    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Define, options, &parse_define_final_check);
1433   
1434    CYG_REPORT_RETVAL(result);
1435    return result;
1436}
1437
1438
1439// ----------------------------------------------------------------------------
1440// syntax: define_format <string>
1441//
1442// FIXME: it is possible to apply some checks to the string, e.g. that there
1443// is only one conversion operation.
1444//
1445// FIXME: also check that the flavor is sensible, define_format has no effect
1446// for none or bool
1447//
1448// FIXME: enforce mutual exclusion with no_define
1449
1450int
1451CdlDefinableBody::parse_define_format(CdlInterpreter interp, int argc, const char* argv[])
1452{
1453    CYG_REPORT_FUNCNAMETYPE("parse_format", "result %d");
1454
1455    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_DefineFormat, 0, 0);
1456   
1457    CYG_REPORT_RETVAL(result);
1458    return result;
1459}
1460// ----------------------------------------------------------------------------
1461// syntax: define_proc <tclcode>
1462int
1463CdlDefinableBody::parse_define_proc(CdlInterpreter interp, int argc, const char* argv[])
1464{
1465    CYG_REPORT_FUNCNAMETYPE("parse_define_proc", "result %d");
1466
1467    int result = CdlParse::parse_tclcode_property(interp, argc, argv, CdlPropertyId_DefineProc, 0, 0);
1468   
1469    CYG_REPORT_RETVAL(result);
1470    return result;
1471}
1472
1473// ----------------------------------------------------------------------------
1474// Syntax: if_define sym1 sym2
1475
1476static void
1477parse_if_define_final_check(CdlInterpreter interp, CdlProperty_StringVector prop)
1478{
1479    CYG_REPORT_FUNCNAME("parse_if_define_final_check");
1480    CYG_PRECONDITION_CLASSC(interp);
1481    CYG_PRECONDITION_CLASSC(prop);
1482   
1483    // There should be exactly two entries in the vector, and both of them should be
1484    // valid preprocessor symbols.
1485    const std::vector<std::string>& strings     = prop->get_strings();
1486
1487    if (2 != strings.size()) {
1488        CdlParse::report_property_parse_error(interp, prop, "There should be exactly two arguments.");
1489    }
1490    if (!Cdl::is_valid_c_preprocessor_symbol(strings[0])) {
1491        CdlParse::report_property_parse_error(interp, prop, strings[0] + " is not a valid C preprocessor symbol.");
1492    }
1493    if (!Cdl::is_valid_c_preprocessor_symbol(strings[1])) {
1494        CdlParse::report_property_parse_error(interp, prop, strings[1] + " is not a valid C preprocessor symbol.");
1495    }
1496   
1497    // There may be a file option. At this stage the only valid filename
1498    // that can be used here is system.h
1499    std::string file_option = prop->get_option("file");
1500    if (("" != file_option) && ("system.h" != file_option)) {
1501        CdlParse::report_property_parse_error(interp, prop, "Invalid -file option " + file_option);
1502    }
1503}
1504
1505int
1506CdlDefinableBody::parse_if_define(CdlInterpreter interp, int argc, const char* argv[])
1507{
1508    CYG_REPORT_FUNCNAMETYPE("parse_if_define", "result %d");
1509
1510    char* options[] = {
1511        "file:",
1512        0
1513    };
1514    int result = CdlParse::parse_stringvector_property(interp, argc, argv, CdlPropertyId_IfDefine, options,
1515                                                       &parse_if_define_final_check, false);
1516   
1517    CYG_REPORT_RETVAL(result);
1518    return result;
1519}
1520
1521//}}}
1522//{{{  generate_config_header()                         
1523
1524// ----------------------------------------------------------------------------
1525// This code needs to allow for the following properties.
1526//
1527// 1) no_define. This suppresses the default #define generation.
1528//
1529// 2) define_format <format_string.
1530//
1531// 3) define [-file <filename>][-format <format_string>] symbol
1532//
1533// 4) define_proc
1534//
1535// 5) if_define
1536
1537void
1538CdlDefinableBody::generate_config_header(Tcl_Channel this_hdr, Tcl_Channel system_h) const
1539{
1540    CYG_REPORT_FUNCNAME("CdlDefinable::generate_config_header");
1541    CYG_REPORT_FUNCARG1XV(this);
1542    CYG_PRECONDITION_THISC();
1543
1544    CdlLoadable    loadable = get_owner();
1545    CdlInterpreter interp   = loadable->get_interpreter();
1546   
1547    // This definable is known to be active. However it may or may not be enabled.
1548    CYG_PRECONDITIONC(is_active());
1549
1550    std::string      name   = get_name();
1551    CdlValueFlavor   flavor = CdlValueFlavor_Bool;
1552    std::string      value  = "1";
1553    CdlConstValuable valuable        = dynamic_cast<CdlConstValuable>(this);
1554    if (0 != valuable) {
1555        // It is always possible to check the enabled() flag.
1556        if (!valuable->is_enabled()) {
1557            CYG_REPORT_RETURN();
1558            return;
1559        }
1560        // The value is only valid for BoolData and Data flavors, and may
1561        // not have been provided. If there is no value then this option
1562        // should not generate a #define
1563        flavor = valuable->get_flavor();
1564        if ((CdlValueFlavor_BoolData == flavor) || (CdlValueFlavor_Data == flavor)) {
1565            value = valuable->get_value();
1566        }
1567    }
1568
1569    // Flavor and value are now both set to sensible strings.
1570    // First, check the no_define property. If this is present then the default
1571    // #define generation should be suppressed.
1572    if (!has_property(CdlPropertyId_NoDefine)) {
1573
1574        // OK, it is necessary to generate at least one #define.
1575        // If this node is actually a loadable then the #define should go
1576        // into system.h, otherwise into the current header
1577        Tcl_Channel chan = this_hdr;
1578        if (dynamic_cast<CdlConstLoadable>((CdlConstNode)this) == loadable) {
1579            chan = system_h;
1580        }
1581
1582        // For flavors None and Bool, there should be just one #define
1583        if ((CdlValueFlavor_None == flavor) || (CdlValueFlavor_Bool == flavor)) {
1584            std::string define = "#define " + name + " 1\n";
1585            Tcl_Write(chan, const_cast<char*>(define.c_str()), -1);
1586        } else {
1587            // If there is a format string then that controls the default
1588            // value display.
1589            if (!has_property(CdlPropertyId_DefineFormat)) {
1590                std::string define = "#define " + name + " " + value + "\n";
1591                Tcl_Write(chan, const_cast<char*>(define.c_str()), -1);
1592            } else {
1593                CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(get_property(CdlPropertyId_DefineFormat));
1594                CYG_ASSERT_CLASSC(strprop);
1595                std::string format = strprop->get_string();
1596                std::string cmd = "return \"#define " + name + " [format " + format + " " + value + "]\n\"";
1597                std::string define;
1598                if (TCL_OK != interp->eval(cmd, define)) {
1599                    throw CdlInputOutputException("Internal error executing tcl fragment to process define_format property");
1600                }
1601                Tcl_Write(chan, const_cast<char*>(define.c_str()), -1);
1602            }
1603
1604            // There may also be a separate #define of the form <name>_<value>,
1605            // if that is a valid preprocessor symbol.
1606            std::string tmp = name + "_" + value;
1607            if (Cdl::is_valid_c_preprocessor_symbol(tmp)) {
1608                tmp = "#define "+ tmp + "\n";
1609                Tcl_Write(chan, const_cast<char*>(tmp.c_str()), -1);
1610            }
1611           
1612            // For loadables, add additional version information to system_h
1613            if (dynamic_cast<CdlConstLoadable>((CdlConstNode)this) == loadable) {
1614                system_h_add_package_versioning(system_h, name, value);
1615            }
1616        }
1617    }
1618
1619    // Next, check for any additional define properties
1620    std::vector<CdlProperty> define_props;
1621    get_properties(CdlPropertyId_Define, define_props);
1622    std::vector<CdlProperty>::const_iterator prop_i;
1623    for (prop_i = define_props.begin(); prop_i != define_props.end(); prop_i++) {
1624        CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(*prop_i);
1625        CYG_ASSERT_CLASSC(strprop);
1626        std::string symbol = strprop->get_string();
1627
1628        std::string file = strprop->get_option("file");
1629        Tcl_Channel chan = this_hdr;
1630        if (("" != file) && ("system.h" == file)) {
1631            chan = system_h;
1632        }
1633
1634        if ((CdlValueFlavor_None == flavor) || (CdlValueFlavor_Bool == flavor)) {
1635            std::string define = "#define " + symbol + " 1\n";
1636            Tcl_Write(chan, const_cast<char*>(define.c_str()), -1);
1637        } else {
1638            std::string format = strprop->get_option("format");
1639            if ("" == format) {
1640                std::string define = "#define " + symbol + " " + value + "\n";
1641                Tcl_Write(chan, const_cast<char*>(define.c_str()), -1);
1642            } else {
1643                std::string cmd = "return \"#define " + symbol + " [format " + format + " " + value + "]\n\"";
1644                std::string define;
1645                if (TCL_OK != interp->eval(cmd, define)) {
1646                    throw CdlInputOutputException("Internal error executing tcl fragment to process format option");
1647                }
1648                Tcl_Write(chan, const_cast<char*>(define.c_str()), -1);
1649            }
1650
1651            std::string tmp = symbol + "_" + value;
1652            if (Cdl::is_valid_c_preprocessor_symbol(tmp)) {
1653                tmp = "#define " + tmp + "\n";
1654                Tcl_Write(chan, const_cast<char*>(tmp.c_str()), -1);
1655            }
1656        }
1657    }
1658
1659    // Now check for if_define properties
1660    std::vector<CdlProperty> if_define_props;
1661    get_properties(CdlPropertyId_IfDefine, if_define_props);
1662    for (prop_i = if_define_props.begin(); prop_i != if_define_props.end(); prop_i++) {
1663        CdlProperty_StringVector strprop = dynamic_cast<CdlProperty_StringVector>(*prop_i);
1664        CYG_ASSERT_CLASSC(strprop);
1665        CYG_ASSERTC(2 == strprop->get_number_of_strings());
1666
1667        std::string sym1 = strprop->get_string(0);
1668        std::string sym2 = strprop->get_string(1);
1669
1670        Tcl_Channel chan = this_hdr;
1671        std::string file = strprop->get_option("file");
1672        if (("" != file) && ("system.h" == file)) {
1673            chan = system_h;
1674        }
1675        std::string data = "#ifdef " + sym1 + "\n# define " + sym2 + " 1\n#endif\n";
1676        Tcl_Write(chan, const_cast<char*>(data.c_str()), -1);
1677    }
1678
1679    // And define_proc properties
1680    std::vector<CdlProperty> define_proc_props;
1681    get_properties(CdlPropertyId_DefineProc, define_proc_props);
1682    for (prop_i = define_proc_props.begin(); prop_i != define_proc_props.end(); prop_i++) {
1683        CdlProperty_TclCode codeprop = dynamic_cast<CdlProperty_TclCode>(*prop_i);
1684        CYG_ASSERT_CLASSC(codeprop);
1685
1686        cdl_tcl_code code       = codeprop->get_code();
1687        std::string  result;
1688        if (TCL_OK != interp->eval(code, result)) {
1689            throw CdlInputOutputException("Error evaluating define_proc property for " + name + "\n" + result);
1690        }
1691    }
1692   
1693   
1694    CYG_REPORT_RETURN();
1695}
1696
1697//}}}
1698
1699//}}}
1700//{{{  CdlDefineLoadableBody           
1701
1702//{{{  Basics                           
1703
1704// ----------------------------------------------------------------------------
1705
1706CdlDefineLoadableBody::CdlDefineLoadableBody()
1707{
1708    CYG_REPORT_FUNCNAME("CdlDefineLoadable:: default constructor");
1709    CYG_REPORT_FUNCARG1XV(this);
1710
1711    cdldefineloadablebody_cookie = CdlDefineLoadableBody_Magic;
1712    CYGDBG_MEMLEAK_CONSTRUCTOR();
1713   
1714    CYG_POSTCONDITION_THISC();
1715    CYG_REPORT_RETURN();
1716}
1717
1718CdlDefineLoadableBody::~CdlDefineLoadableBody()
1719{
1720    CYG_REPORT_FUNCNAME("CdlDefineLoadable:: destructor");
1721    CYG_REPORT_FUNCARG1XV(this);
1722    CYG_PRECONDITION_THISC();
1723
1724    cdldefineloadablebody_cookie = CdlDefineLoadableBody_Invalid;
1725    CYGDBG_MEMLEAK_DESTRUCTOR();
1726   
1727    CYG_REPORT_RETURN();
1728}
1729
1730// ----------------------------------------------------------------------------
1731
1732std::string
1733CdlDefineLoadableBody::get_class_name() const
1734{
1735    CYG_REPORT_FUNCNAME("CdlDefineLoadable::get_class_name");
1736    CYG_PRECONDITION_THISC();
1737    CYG_REPORT_RETURN();
1738    return "define_loadable";
1739}
1740
1741// ----------------------------------------------------------------------------
1742
1743bool
1744CdlDefineLoadableBody::check_this(cyg_assert_class_zeal zeal) const
1745{
1746    if (CdlDefineLoadableBody_Magic != cdldefineloadablebody_cookie) {
1747        return false;
1748    }
1749    CYGDBG_MEMLEAK_CHECKTHIS();
1750    return CdlLoadableBody::check_this(zeal) && CdlNodeBody::check_this(zeal);
1751}
1752
1753//}}}
1754//{{{  Property parsing                 
1755
1756// ----------------------------------------------------------------------------
1757
1758void
1759CdlDefineLoadableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
1760{
1761    CYG_REPORT_FUNCNAME("CdlDefineLoadable::add_property_parsers");
1762
1763    static CdlInterpreterCommandEntry commands[] =
1764    {
1765        CdlInterpreterCommandEntry("define_header",      &parse_define_header),
1766        CdlInterpreterCommandEntry("",                   0                   )
1767    };
1768
1769    for (int i = 0; commands[i].command != 0; i++) {
1770        std::vector<CdlInterpreterCommandEntry>::const_iterator j;
1771        for (j = parsers.begin(); j != parsers.end(); j++) {
1772            if (commands[i].name == j->name) {
1773                if (commands[i].command != j->command) {
1774                    CYG_FAIL("Property names are being re-used");
1775                }
1776                break;
1777            }
1778        }
1779        if (j == parsers.end()) {
1780            parsers.push_back(commands[i]);
1781        }
1782    }
1783    CdlNodeBody::add_property_parsers(parsers);
1784   
1785    CYG_REPORT_RETURN();
1786}
1787
1788void
1789CdlDefineLoadableBody::check_properties(CdlInterpreter interp)
1790{
1791    CYG_REPORT_FUNCNAME("CdlDefineLoadable::check_properties");
1792    CYG_REPORT_FUNCARG2XV(this, interp);
1793    CYG_PRECONDITION_THISC();
1794    CYG_PRECONDITION_CLASSC(interp);
1795
1796    // There should be at most one define_header property
1797    int count = count_properties(CdlPropertyId_DefineHeader);
1798    if (count> 1) {
1799        CdlParse::report_error(interp, "", "There should be at most one define_header property.");
1800    }
1801    // FIXME: filename validation
1802   
1803    CdlNodeBody::check_properties(interp);
1804   
1805    CYG_REPORT_RETURN();
1806}
1807
1808// ----------------------------------------------------------------------------
1809// syntax: define_header <header file name>
1810int
1811CdlDefineLoadableBody::parse_define_header(CdlInterpreter interp, int argc, const char* argv[])
1812{
1813    CYG_REPORT_FUNCNAMETYPE("parse_define_header", "result %d");
1814
1815    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_DefineHeader, 0, 0);
1816   
1817    CYG_REPORT_RETVAL(result);
1818    return result;
1819}
1820
1821//}}}
1822//{{{  generate_config_header()         
1823
1824// ----------------------------------------------------------------------------
1825void
1826CdlDefineLoadableBody::generate_config_header(Tcl_Channel this_hdr, Tcl_Channel system_h) const
1827{
1828    CYG_REPORT_FUNCNAME("CdlDefineLoadable::generate_config_header");
1829    CYG_REPORT_FUNCARG1XV(this);
1830    CYG_PRECONDITION_THISC();
1831
1832    CdlInterpreter interp       = get_interpreter();
1833    Tcl_RegisterChannel(interp->get_tcl_interpreter(), this_hdr);
1834    Tcl_RegisterChannel(interp->get_tcl_interpreter(), system_h);
1835
1836    CdlInterpreterBody::ContextSupport(interp, std::string("Package ") + this->get_name() + ", header file generation");
1837   
1838    try {
1839        interp->set_variable("::cdl_header", Tcl_GetChannelName(this_hdr));
1840        interp->set_variable("::cdl_system_header", Tcl_GetChannelName(system_h));
1841
1842        const std::vector<CdlNode>& contents = get_owned();
1843        std::vector<CdlNode>::const_iterator node_i;
1844        for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
1845            CdlDefinable definable = dynamic_cast<CdlDefinable>(*node_i);
1846            if (0 == definable) {
1847                continue;
1848            }
1849            if (!definable->is_active()) {
1850                continue;
1851            }
1852            definable->generate_config_header(this_hdr, system_h);
1853        }
1854   
1855        interp->unset_variable("::cdl_header");
1856        interp->unset_variable("::cdl_system_header");
1857    } catch(...) {
1858        Tcl_UnregisterChannel(interp->get_tcl_interpreter(), this_hdr);
1859        Tcl_UnregisterChannel(interp->get_tcl_interpreter(), system_h);
1860        throw;
1861    }
1862   
1863    Tcl_UnregisterChannel(interp->get_tcl_interpreter(), this_hdr);
1864    Tcl_UnregisterChannel(interp->get_tcl_interpreter(), system_h);
1865   
1866    CYG_REPORT_RETURN();
1867}
1868
1869//}}}
1870//{{{  get_config_headers()             
1871
1872// ----------------------------------------------------------------------------
1873// What header file should be generated for this loadable?
1874//
1875// If there is a define_header property then this should be used.
1876// Otherwise a filename is constructed from the loadable's name.
1877
1878std::string
1879CdlDefineLoadableBody::get_config_header() const
1880{
1881    CYG_REPORT_FUNCNAME("CdlDefineLoadable::get_config_headers");
1882    CYG_REPORT_FUNCARG1XV(this);
1883    CYG_PRECONDITION_THISC();
1884
1885    std::string result = "";
1886    CdlProperty prop = get_property(CdlPropertyId_DefineHeader);
1887    if (0 != prop) {
1888        CdlProperty_String string_prop = dynamic_cast<CdlProperty_String>(prop);
1889        CYG_ASSERT_CLASSC(string_prop);
1890        result = string_prop->get_string();
1891    } else {
1892        std::string tmp = get_name();
1893        result = Cdl::get_short_form(tmp);
1894        result += ".h";
1895    }
1896    CYG_REPORT_RETURN();
1897    return result;
1898}
1899
1900//}}}
1901
1902//}}}
1903//{{{  CdlToplevel                     
1904
1905//{{{  CdlToplevel::get_build_info()           
1906
1907// ----------------------------------------------------------------------------
1908// Essentially this code involves iterating over the loadables vector,
1909// looking for BuildLoadables and invoking their update_build_info()
1910// member function. In addition, if there is currently some data in
1911// the build_info vector (probably from a previous call) then that
1912// must be cleared.
1913
1914void
1915CdlToplevelBody::get_build_info(CdlBuildInfo& build_info)
1916{
1917    CYG_REPORT_FUNCNAME("CdlToplevel::get_build_info");
1918    CYG_REPORT_FUNCARG2XV(this, &build_info);
1919    CYG_PRECONDITION_THISC();
1920
1921    if (0 != build_info.entries.size()) {
1922        build_info.entries.clear();
1923    }
1924
1925    const std::vector<CdlLoadable>&          loadables   = get_loadables();
1926    std::vector<CdlLoadable>::const_iterator load_i;
1927    for (load_i = loadables.begin(); load_i != loadables.end(); load_i++) {
1928        CdlConstBuildLoadable bl = dynamic_cast<CdlConstBuildLoadable>(*load_i);
1929        if (0 != bl) {
1930            bl->update_build_info(build_info);
1931        }
1932    }
1933
1934    CYG_REPORT_RETURN();
1935}
1936
1937//}}}
1938//{{{  CdlToplevel::get_all_build_info()       
1939
1940// ----------------------------------------------------------------------------
1941// This is just like get_build_info(), but calls a different
1942// BuildLoadable member.
1943
1944void
1945CdlToplevelBody::get_all_build_info(CdlBuildInfo& build_info)
1946{
1947    CYG_REPORT_FUNCNAME("CdlToplevel::get_all_build_info");
1948    CYG_REPORT_FUNCARG2XV(this, &build_info);
1949    CYG_PRECONDITION_THISC();
1950
1951    if (0 != build_info.entries.size()) {
1952        build_info.entries.clear();
1953    }
1954
1955    const std::vector<CdlLoadable>&          loadables   = get_loadables();
1956    std::vector<CdlLoadable>::const_iterator load_i;
1957    for (load_i = loadables.begin(); load_i != loadables.end(); load_i++) {
1958        CdlConstBuildLoadable bl = dynamic_cast<CdlConstBuildLoadable>(*load_i);
1959        if (0 != bl) {
1960            bl->update_all_build_info(build_info);
1961        }
1962    }
1963
1964    CYG_REPORT_RETURN();
1965}
1966
1967//}}}
1968//{{{  CdlToplevel::generate_config_headers()   
1969
1970// ----------------------------------------------------------------------------
1971// Generating the config headers. This involves the following steps:
1972//
1973// 1) for every DefineLoadable, find out what header file should
1974//    be generated. Note that some loadables may share a header file.
1975//
1976// 2) create a temporary version of system.h. Note that it is not
1977//    a good idea to just overwrite an existing system.h, chances
1978//    are pretty good that the file will not have changed, and
1979//    updating it unnecessarily will result in unnecessary rebuilds
1980//    due to header file dependencies.
1981//
1982// 3) for each file that should be generated, create a temporary
1983//    version and allow all applicable loadables to update it.
1984
1985// A utility to compare two files and do the right thing.
1986// This requires some simple Tcl code.
1987static void
1988compare_and_copy(CdlInterpreter interp, std::string file1, std::string file2)
1989{
1990    CYG_REPORT_FUNCNAME("compare_and_copy");
1991    CYG_PRECONDITION_CLASSC(interp);
1992    CYG_PRECONDITIONC("" != file1);
1993    CYG_PRECONDITIONC("" != file2);
1994    CYG_PRECONDITIONC(file1 != file2);
1995
1996    static char compare_and_copy_script[] = "\
1997if {[file exists \"$::cdl_compare_and_copy_file2\"] == 0} {                                   \n\
1998    catch { file rename -- $::cdl_compare_and_copy_file1 $::cdl_compare_and_copy_file2}       \n\
1999    return                                                                                    \n\
2000}                                                                                             \n\
2001set fd [open \"$::cdl_compare_and_copy_file1\" r]                                             \n\
2002set data1 [read $fd]                                                                          \n\
2003close $fd                                                                                     \n\
2004set fd [open \"$::cdl_compare_and_copy_file2\" r]                                             \n\
2005set data2 [read $fd]                                                                          \n\
2006close $fd                                                                                     \n\
2007if {$data1 == $data2} {                                                                       \n\
2008    file delete \"$::cdl_compare_and_copy_file1\"                                             \n\
2009} else {                                                                                      \n\
2010    catch { file rename -force -- $::cdl_compare_and_copy_file1 $::cdl_compare_and_copy_file2 } \n\
2011}                                                                                             \n\
2012";
2013
2014    interp->set_variable("::cdl_compare_and_copy_file1", file1);
2015    interp->set_variable("::cdl_compare_and_copy_file2", file2);
2016    std::string tcl_result;
2017    if (TCL_OK != interp->eval(compare_and_copy_script, tcl_result)) {
2018        throw CdlInputOutputException("internal error manipulating temporary header " + file1 + " and target " + file2 +
2019            "\n" + tcl_result);
2020    }
2021}
2022
2023void
2024CdlToplevelBody::generate_config_headers(std::string directory)
2025{
2026    CYG_REPORT_FUNCNAME("CdlToplevel::generate_config_headers");
2027    CYG_REPORT_FUNCARG1XV(this);
2028    CYG_PRECONDITION_THISC();
2029    CYG_ASSERTC("" != directory);
2030
2031    // Replace any backslashes in the path with forward slashes. The
2032    // latter are used throughout the library
2033    // NOTE: this is not i18n-friendly.
2034    for (unsigned int i = 0; i < directory.size(); i++) {
2035        if ('\\' == directory[i]) {
2036            directory[i] = '/';
2037        }
2038    }
2039   
2040    CdlInterpreter interp = get_interpreter();
2041    std::string    tcl_result;
2042    if ((TCL_OK != interp->eval("file isdirectory \"" + directory + "\"", tcl_result)) ||
2043        (tcl_result != "1")) {
2044        throw CdlInputOutputException("target " + directory + " is not a valid existing directory.");
2045    }
2046   
2047    std::vector<std::pair<CdlDefineLoadable, std::string> > headers;
2048    const std::vector<CdlLoadable>& loadables = get_loadables();
2049    std::vector<CdlLoadable>::const_iterator load_i;
2050    for (load_i = loadables.begin(); load_i != loadables.end(); load_i++) {
2051        CdlDefineLoadable tmp = dynamic_cast<CdlDefineLoadable>(*load_i);
2052        if (0 != tmp) {
2053            std::string hdr = tmp->get_config_header();
2054            headers.push_back(std::make_pair(tmp, hdr));
2055        }
2056    }
2057
2058    static char banner_format[] =
2059"#ifndef CYGONCE_PKGCONF_%s\n\
2060#define CYGONCE_PKGCONF_%s\n\
2061/*\n\
2062 * File <pkgconf/%s>\n\
2063 *\n\
2064 * This file is generated automatically by the configuration\n\
2065 * system. It should not be edited. Any changes to this file\n\
2066 * may be overwritten.\n\
2067 */\n\
2068\n";
2069#ifdef _WIN32
2070    // Create three channels which Tcl will use for standard streams
2071    // if these streams do not already exist. This avoids a Tcl
2072    // problem which can prevent closure of system.h. (FIXME)
2073    Tcl_Channel stdin_chan = Tcl_OpenFileChannel(interp->get_tcl_interpreter(), "nul", "w", 0666);
2074    Tcl_Channel stdout_chan = Tcl_OpenFileChannel(interp->get_tcl_interpreter(), "nul", "w", 0666);
2075    Tcl_Channel stderr_chan = Tcl_OpenFileChannel(interp->get_tcl_interpreter(), "nul", "w", 0666);
2076    Tcl_RegisterChannel(0, stdin_chan);
2077    Tcl_RegisterChannel(0, stdout_chan);
2078    Tcl_RegisterChannel(0, stderr_chan);
2079#endif
2080    // Assume for now that files __libcdl_file1 and __libcdl_file2 are
2081    // legal on all platforms of interest, and that nobody is going to
2082    // export to these.
2083    std::string system_h_name = directory + "/__libcdl_file1";
2084    Tcl_Channel system_h = Tcl_OpenFileChannel(interp->get_tcl_interpreter(),
2085                                               const_cast<char*>(system_h_name.c_str()), "w", 0666);
2086    if (0 == system_h) {
2087        throw CdlInputOutputException("Unable to open file " + system_h_name + "\n" +
2088                                      interp->get_result());
2089    }
2090    // The channel will be registered and unregistered in several
2091    // different interpreters. This call prevents the channel from
2092    // disappearing prematurely.
2093    Tcl_RegisterChannel(0, system_h);
2094
2095    // Make sure that this operation is undone if necessary.
2096    try {
2097        // Now fill in system.h with the appropriate data. Start with the banner.
2098        char local_buf[512];
2099        sprintf(local_buf, banner_format, "SYSTEM_H", "SYSTEM_H", "system.h");
2100        Tcl_Write(system_h, local_buf, -1);
2101
2102        // Add generic version information
2103        system_h_add_version_header(system_h);
2104       
2105        // The rest of system.h will be filled in by the following loop.
2106        //
2107        // Walk down the previously constructed headers vector, create
2108        // appropriate files, and let each DefineLoadable fill in the
2109        // file for itself.
2110        std::vector<std::pair<CdlDefineLoadable, std::string> >::iterator outer_i;
2111        std::vector<std::pair<CdlDefineLoadable, std::string> >::iterator inner_i;
2112        for (outer_i = headers.begin(); outer_i != headers.end(); outer_i++) {
2113            if ("" == outer_i->second) {
2114                continue;
2115            }
2116            std::string target_name = outer_i->second;
2117            std::string header_name = directory + "/__libcdl_file2";
2118            Tcl_Channel header_h = Tcl_OpenFileChannel(interp->get_tcl_interpreter(),
2119                                                       const_cast<char*>(header_name.c_str()), "w", 0666);
2120            if (0 == header_h) {
2121                throw CdlInputOutputException("Unable to open file " + header_name + "\n" +
2122                                              interp->get_result());
2123            }
2124            // The channel may be used in several different interpreters, so
2125            // do an extra register operation
2126            Tcl_RegisterChannel(0, header_h);
2127
2128            try {
2129                // Output the banner. This requires an all-upper-case version of the
2130                // header name.
2131                std::string upper_case;
2132                for (unsigned int i = 0; i < target_name.size(); i++) {
2133                    if (islower(target_name[i])) {
2134                        upper_case += toupper(target_name[i]);
2135                    } else if ('.' == target_name[i]) {
2136                        upper_case += '_';
2137                    } else {
2138                        upper_case += target_name[i];
2139                    }
2140                }
2141                sprintf(local_buf, banner_format, upper_case.c_str(), upper_case.c_str(), target_name.c_str());
2142                Tcl_Write(header_h, local_buf, -1);
2143
2144                // Now iterate over all the loadables looking for ones which
2145                // should generate #define's for this header, and invoke the
2146                // appropriate member function.
2147                for (inner_i = outer_i; inner_i != headers.end(); inner_i++) {
2148                    if (inner_i->second == target_name) {
2149                        inner_i->first->generate_config_header(header_h, system_h);
2150                        inner_i->second = "";
2151                    }
2152                }
2153       
2154                // The header file has now been updated. Close it and decide whether
2155                // or not to replace the old version
2156                Tcl_Write(header_h, "\n#endif\n", -1);
2157            } catch(...) {
2158                Tcl_UnregisterChannel(0, header_h);
2159                throw;
2160            }
2161            Tcl_UnregisterChannel(0, header_h);
2162            compare_and_copy(interp, header_name, directory + "/" + target_name);
2163        }
2164       
2165        Tcl_Write(system_h, "\n#endif\n", -1);
2166    } catch(...) {
2167        Tcl_UnregisterChannel(0, system_h);
2168        throw;
2169    }
2170   
2171    // This call to UnregisterChannel automatically closes the
2172    // channel, there is no need for an explicit Tcl_Close() call.
2173    Tcl_UnregisterChannel(0, system_h);
2174#ifdef _WIN32
2175    Tcl_UnregisterChannel(0, stderr_chan);
2176    Tcl_UnregisterChannel(0, stdout_chan);
2177    Tcl_UnregisterChannel(0, stdin_chan);
2178#endif
2179    compare_and_copy(interp, system_h_name, directory +"/system.h");
2180}
2181
2182//}}}
2183//{{{  CdlToplevel::get_config_headers()       
2184
2185// ----------------------------------------------------------------------------
2186// Return details of the header files that should be generated. This
2187// allows higher-level code to detect files that should no longer
2188// be present, amongst other uses.
2189//
2190// The main complication is that some packages may wish to share the
2191// same header file, especially hardware packages.
2192
2193void
2194CdlToplevelBody::get_config_headers(std::vector<std::string>& headers)
2195{
2196    CYG_REPORT_FUNCNAME("CdlToplevelBody::get_config_headers");
2197    CYG_REPORT_FUNCARG2XV(this, &headers);
2198    CYG_PRECONDITION_THISC();
2199
2200    // Allow the vector argument to be re-used in multiple calls.
2201    // Strictly speaking this is better done at the application
2202    // level, but the behaviour is consistent with get_build_info();
2203    headers.clear();
2204
2205    // There will always be a system.h header file with details
2206    // of the loadables.
2207    // FIXME: the name of this file should probably be controllable
2208    headers.push_back("system.h");
2209
2210    // Now check each loadable and adds its header file, assuming
2211    // this is unique.
2212    const std::vector<CdlLoadable>& loadables = get_loadables();
2213    std::vector<CdlLoadable>::const_iterator i;
2214    for (i = loadables.begin(); i != loadables.end(); i++) {
2215        CdlDefineLoadable current = dynamic_cast<CdlDefineLoadable>(*i);
2216        if (0 != current) {
2217            std::string its_file = current->get_config_header();
2218            CYG_LOOP_INVARIANTC("" != its_file);
2219            if (std::find(headers.begin(), headers.end(), its_file) == headers.end()) {
2220                headers.push_back(its_file);
2221            }
2222        }
2223    }
2224
2225    CYG_REPORT_RETURN();
2226}
2227
2228//}}}
2229//{{{  CdlToplevel::generate_build_tree()       
2230
2231void
2232CdlToplevelBody::generate_build_tree(std::string build_tree, std::string install_tree)
2233{
2234}
2235
2236//}}}
2237
2238//}}}
Note: See TracBrowser for help on using the repository browser.