]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - tools/src/libcdl/build.cxx
Initial revision
[karo-tx-redboot.git] / tools / src / libcdl / build.cxx
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
61 //}}}
62
63 //{{{  Statics                          
64
65 // ----------------------------------------------------------------------------
66 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlBuildLoadableBody);
67 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlBuildableBody);
68 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlDefineLoadableBody);
69 CYGDBG_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
81 CdlBuildableBody::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
94 CdlBuildableBody::~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
108 std::string
109 CdlBuildableBody::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
119 bool
120 CdlBuildableBody::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
134 void
135 CdlBuildableBody::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
168 void
169 CdlBuildableBody::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
211 int
212 CdlBuildableBody::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
240 bool
241 CdlBuildableBody::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
358 static void
359 parse_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
390 int
391 CdlBuildableBody::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
411 static void
412 parse_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
444 int
445 CdlBuildableBody::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
465 int
466 CdlBuildableBody::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
483 int
484 CdlBuildableBody::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.
500 void
501 CdlBuildableBody::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
535 void
536 CdlBuildableBody::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     CYG_ASSERTC("" != directory);
548     CdlInterpreter interp       = loadable->get_interpreter();
549     CYG_ASSERT_CLASSC(interp);
550
551     // The interpreter needs some information about the locations
552     // of various things. This code has to be kept in step with
553     // CdlLoadable::find_relative_file()
554     interp->set_variable("::cdl_topdir", get_toplevel()->get_directory());
555     interp->set_variable("::cdl_pkgdir", directory);
556
557     // For many packages the sources will reside in a src subdirectory.
558     // For simple packages the sources may live directly at the toplevel
559     bool has_src_subdir    = loadable->has_subdirectory("src");
560
561     // NOTE: the object property is not yet supported
562     std::vector<CdlProperty> compile_properties;
563     get_properties(CdlPropertyId_Compile, compile_properties);
564     std::vector<CdlProperty> makeobject_properties;
565     get_properties(CdlPropertyId_MakeObject, makeobject_properties);
566     std::vector<CdlProperty> make_properties;
567     get_properties(CdlPropertyId_Make, make_properties);
568     std::vector<CdlProperty>::const_iterator prop_i;
569
570     for (prop_i = compile_properties.begin(); prop_i != compile_properties.end(); prop_i++) {
571         CdlProperty_StringVector compile_prop = dynamic_cast<CdlProperty_StringVector>(*prop_i);
572         CYG_LOOP_INVARIANT_CLASSC(compile_prop);
573
574         // Does this property have a library option?
575         std::string current_library = compile_prop->get_option("library");
576         if ("" == current_library) {
577             current_library = package_library;
578         }
579
580         const std::vector<std::string>& files   = compile_prop->get_strings();
581         std::vector<std::string>::const_iterator file_i;
582
583         for (file_i = files.begin(); file_i != files.end(); file_i++) {
584
585             // For each listed file, try to find it. If this is unsuccessful
586             // then assume that the file will be generated later on.
587             std::string path = loadable->find_relative_file(*file_i, "src");
588             if ("" == path) {
589                 if (has_src_subdir) {
590                     path = "src/" + *file_i;
591                 } else {
592                     path = *file_i;
593                 }
594             }
595
596             // Now check whether or not the specified file is already present.
597             std::vector<CdlBuildInfo_Compile>::const_iterator info_i;
598             for (info_i = build_info.compiles.begin(); info_i != build_info.compiles.end(); info_i++) {
599                 if ((current_library == info_i->library) && (path == info_i->source)) {
600                     break;
601                 }
602             }
603             if (info_i == build_info.compiles.end()) {
604                 CdlBuildInfo_Compile new_info;
605                 new_info.library    = current_library;
606                 new_info.source     = path;
607                 build_info.compiles.push_back(new_info);
608             }
609         }
610     }
611
612     for (prop_i = makeobject_properties.begin(); prop_i != makeobject_properties.end(); prop_i++) {
613         CdlProperty_String prop = dynamic_cast<CdlProperty_String>(*prop_i);
614         CYG_LOOP_INVARIANT_CLASSC(prop);
615
616         // Does thie property have a library option?
617         std::string current_library = prop->get_option("library");
618         if ("" == current_library) {
619             current_library = package_library;
620         }
621
622         // How about a priority field? The default priority for make_object is 100
623         // We can rely on the validation done during the parsing process
624         cdl_int priority = 100;
625         std::string priority_option = prop->get_option("priority");
626         if ("" != priority_option) {
627             Cdl::string_to_integer(priority_option, priority);
628         }
629
630         // What we need now is the separate target, deps, and rules. These
631         // can be obtained via a utility. The raw data will have been validated
632         // already.
633         std::string raw_data = prop->get_string();
634         std::string target;
635         std::string deps;
636         std::string rules;
637         std::string error_msg;
638         bool result;
639
640         result = CdlBuildableBody::split_custom_build_step(raw_data, target, deps, rules, error_msg);
641         CYG_ASSERTC(true == result);
642
643         // Construct a local object, then copy it into the vector
644         CdlBuildInfo_MakeObject local_copy;
645         local_copy.priority     = priority;
646         local_copy.library      = current_library;
647         local_copy.object       = target;
648         local_copy.deps         = deps;
649         local_copy.rules        = rules;
650
651         build_info.make_objects.push_back(local_copy);
652     }
653     
654     for (prop_i = make_properties.begin(); prop_i != make_properties.end(); prop_i++) {
655         CdlProperty_String prop = dynamic_cast<CdlProperty_String>(*prop_i);
656         CYG_LOOP_INVARIANT_CLASSC(prop);
657
658         // Is there a priority field? The default priority for make is
659         // 300 We can rely on the validation done during the parsing
660         // process
661         cdl_int priority = 300;
662         std::string priority_option = prop->get_option("priority");
663         if ("" != priority_option) {
664             Cdl::string_to_integer(priority_option, priority);
665         }
666
667         // What we need now is the separate target, deps, and rules. These
668         // can be obtained via a utility. The raw data will have been validated
669         // already.
670         std::string raw_data = prop->get_string();
671         std::string target;
672         std::string deps;
673         std::string rules;
674         std::string error_msg;
675         bool result;
676
677         result = CdlBuildableBody::split_custom_build_step(raw_data, target, deps, rules, error_msg);
678         CYG_ASSERTC(true == result);
679
680         // Construct a local object, then copy it into the vector
681         CdlBuildInfo_Make local_copy;
682         local_copy.priority     = priority;
683         local_copy.target       = target;
684         local_copy.deps         = deps;
685         local_copy.rules        = rules;
686
687         build_info.makes.push_back(local_copy);
688     }
689     
690     CYG_REPORT_RETURN();
691 }
692
693 //}}}
694
695 //}}}
696 //{{{  CdlBuildLoadableBody             
697
698 //{{{  Class variables                          
699
700 // ----------------------------------------------------------------------------
701 // This variable controls the default library that should be generated.
702 // Some applications may wish to override this.
703 char* CdlBuildLoadableBody::default_library_name        = "libtarget.a";
704
705 // The pattern that should be used to identify header files.
706 // FIXME: this information should come out of a data file
707 char* CdlBuildLoadableBody::default_headers_glob_pattern = "*.h *.hxx *.inl *.si *.inc";
708
709 //}}}
710 //{{{  The simple stuff                         
711
712 // ----------------------------------------------------------------------------
713
714 CdlBuildLoadableBody::CdlBuildLoadableBody()
715     : CdlLoadableBody()
716 {
717     CYG_REPORT_FUNCNAME("CdlBuildLoadable:: default constructor");
718     CYG_REPORT_FUNCARG1XV(this);
719
720     // There is no data to initialize
721     cdlbuildloadablebody_cookie = CdlBuildLoadableBody_Magic;
722     CYGDBG_MEMLEAK_CONSTRUCTOR();
723     
724     CYG_POSTCONDITION_THISC();
725     CYG_REPORT_RETURN();
726 }
727
728 CdlBuildLoadableBody::~CdlBuildLoadableBody()
729 {
730     CYG_REPORT_FUNCNAME("CdlBuildLoadable:: destructor");
731     CYG_REPORT_FUNCARG1XV(this);
732     CYG_PRECONDITION_THISC();
733
734     cdlbuildloadablebody_cookie = CdlBuildLoadableBody_Invalid;
735     CYGDBG_MEMLEAK_DESTRUCTOR();
736     
737     CYG_REPORT_RETURN();
738 }
739
740 // ----------------------------------------------------------------------------
741
742 std::string
743 CdlBuildLoadableBody::get_class_name() const
744 {
745     CYG_REPORT_FUNCNAME("CdlBuildLoadable::get_class_name");
746     CYG_PRECONDITION_THISC();
747     CYG_REPORT_RETURN();
748     return "build_loadable";
749 }
750
751 // ----------------------------------------------------------------------------
752
753 bool
754 CdlBuildLoadableBody::check_this(cyg_assert_class_zeal zeal) const
755 {
756     if (CdlBuildLoadableBody_Magic != cdlbuildloadablebody_cookie) {
757         return false;
758     }
759     CYGDBG_MEMLEAK_CHECKTHIS();
760     return CdlContainerBody::check_this(zeal) && CdlNodeBody::check_this(zeal);
761 }
762
763 //}}}
764 //{{{  Property parsers                         
765
766 // ----------------------------------------------------------------------------
767
768 void
769 CdlBuildLoadableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
770 {
771     CYG_REPORT_FUNCNAME("CdlBuildLoadable::add_property_parsers");
772
773     static CdlInterpreterCommandEntry commands[] =
774     {
775         CdlInterpreterCommandEntry("library",            &CdlBuildLoadableBody::parse_library       ),
776         CdlInterpreterCommandEntry("makefile",           &CdlBuildLoadableBody::parse_makefile      ),
777         CdlInterpreterCommandEntry("include_dir",        &CdlBuildLoadableBody::parse_include_dir   ),
778         CdlInterpreterCommandEntry("include_files",      &CdlBuildLoadableBody::parse_include_files ),
779         CdlInterpreterCommandEntry("",                   0                                          )
780     };
781
782     for (int i = 0; commands[i].command != 0; i++) {
783         std::vector<CdlInterpreterCommandEntry>::const_iterator j;
784         for (j = parsers.begin(); j != parsers.end(); j++) {
785             if (commands[i].name == j->name) {
786                 if (commands[i].command != j->command) {
787                     CYG_FAIL("Property names are being re-used");
788                 }
789                 break;
790             }
791         }
792         if (j == parsers.end()) {
793             parsers.push_back(commands[i]);
794         }
795     }
796     
797     CYG_REPORT_RETURN();
798 }
799
800 void
801 CdlBuildLoadableBody::check_properties(CdlInterpreter interp)
802 {
803     CYG_REPORT_FUNCNAME("CdlBuildLoadable::check_properties");
804     CYG_REPORT_FUNCARG2XV(this, interp);
805     CYG_PRECONDITION_THISC();
806     CYG_PRECONDITION_CLASSC(interp);
807
808     CdlNodeBody::check_properties(interp);
809     
810     CYG_REPORT_RETURN();
811 }
812
813 // ----------------------------------------------------------------------------
814 // syntax: library <filename>
815 //
816 // NOTE: there should probably be a check that the library name is in
817 // a valid format, i.e. libxxx.a
818
819 int
820 CdlBuildLoadableBody::parse_library(CdlInterpreter interp, int argc, const char* argv[])
821 {
822     CYG_REPORT_FUNCNAMETYPE("parse_library", "result %d");
823
824     int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Library, 0, 0);
825     
826     CYG_REPORT_RETVAL(result);
827     return result;
828 }
829
830 // ----------------------------------------------------------------------------
831 // syntax: makefile <filename>
832 //
833 // NOTE: possibly there should be a check that the makefile exists.
834 // Do we want to allow build_proc's to generate makefiles?
835 int
836 CdlBuildLoadableBody::parse_makefile(CdlInterpreter interp, int argc, const char* argv[])
837 {
838     CYG_REPORT_FUNCNAMETYPE("parse_makefile", "result %d");
839
840     int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Makefile, 0, 0);
841     
842     CYG_REPORT_RETVAL(result);
843     return result;
844 }
845
846 // ----------------------------------------------------------------------------
847 // syntax: include_dir <directory name>
848 int
849 CdlBuildLoadableBody::parse_include_dir(CdlInterpreter interp, int argc, const char* argv[])
850 {
851     CYG_REPORT_FUNCNAMETYPE("parse_include_dir", "result %d");
852
853     int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_IncludeDir, 0, 0);
854     
855     CYG_REPORT_RETVAL(result);
856     return result;
857 }
858
859 // ----------------------------------------------------------------------------
860 // Syntax: include_files <file1 file2 ...>
861 //
862 // This lists the header files that should be copied into the install tree
863 // as part of the build operation. In the absence of an include_files property
864 // there should be an include subdirectory, and all files in that subdirectory
865 // are assumed to be exportable headers.
866 //
867 // NOTE: add a finalizer to check that the files exist or get created.
868
869 int
870 CdlBuildLoadableBody::parse_include_files(CdlInterpreter interp, int argc, const char* argv[])
871 {
872     CYG_REPORT_FUNCNAMETYPE("parse_include_files", "result %d");
873
874     int result = CdlParse::parse_stringvector_property(interp, argc, argv, CdlPropertyId_IncludeFiles, 0, 0, true);
875     
876     CYG_REPORT_RETVAL(result);
877     return result;
878 }
879
880 //}}}
881 //{{{  update_build_info()                      
882
883 // ----------------------------------------------------------------------------
884 // This utility routine takes care of filling in a Buildinfo_Loadable
885 // structure with the appropriate header file information. This involves
886 // the following:
887 //
888 // 1) there may be an include_dir property. This affects the destination
889 //     of any header files that are listed.
890 //
891 // 2) the loadable may or may not have an include subdirectory. For
892 //    non-trivial packages the include subdirectory provides a clean
893 //    way of separating interface and implementation. For simple
894 //    packages it is too heavyweight.
895 //
896 // 3) there may be one or more include_files property. If so then these
897 //    specify all the files that should be exported.
898 //
899 // 4) otherwise if there is an include subdirectory then we need to
900 //    know all the header files in that subdirectory. A Tcl script is
901 //    used for this.
902 //
903 // 5) otherwise all the header files below the package directory itself
904 //    are of interest.
905
906 static void
907 update_header_file_info(CdlConstBuildLoadable loadable, CdlBuildInfo_Loadable& build_info)
908 {
909     CYG_REPORT_FUNCNAME("update_header_file_info");
910     CYG_REPORT_FUNCARG2XV(loadable, &build_info);
911     CYG_PRECONDITION_CLASSC(loadable);
912
913     std::string dest_dir = "";
914     CdlProperty include_dir_prop = loadable->get_property(CdlPropertyId_IncludeDir);
915     if (0 != include_dir_prop) {
916         CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(include_dir_prop);
917         CYG_ASSERT_CLASSC(strprop);
918         dest_dir = strprop->get_string();
919     }
920
921     bool has_include_subdir = loadable->has_subdirectory("include");
922
923     std::vector<CdlProperty> include_file_properties;
924     loadable->get_properties(CdlPropertyId_IncludeFiles, include_file_properties);
925     if (include_file_properties.size() > 0) {
926         std::vector<CdlProperty>::const_iterator prop_i;
927         for (prop_i = include_file_properties.begin(); prop_i != include_file_properties.end(); prop_i++) {
928             
929             CdlProperty_StringVector strvprop = dynamic_cast<CdlProperty_StringVector>(*prop_i);
930             CYG_ASSERT_CLASSC(strvprop);
931
932             const std::vector<std::string>& filenames = strvprop->get_strings();
933             std::vector<std::string>::const_iterator file_i;
934             for (file_i = filenames.begin(); file_i != filenames.end(); file_i++) {
935                 std::string path = loadable->find_relative_file(*file_i, "include");
936                 // Assume that the header file will be generated by a build_proc
937                 if ("" == path) {
938                     if (has_include_subdir) {
939                         path = "include/" + *file_i;
940                     } else {
941                         path = *file_i;
942                     }
943                 }
944                 CdlBuildInfo_Header local_copy;
945                 local_copy.source       = path;
946                 local_copy.destination  = "";
947                 if ("" != dest_dir) {
948                     local_copy.destination = dest_dir + "/";
949                 }
950                 // At this stage "path" may begin with "include/", which should not
951                 // be present in the destination.
952                 const char* tmp = path.c_str();
953                 if (0 == strncmp("include/", tmp, 8)) {
954                     local_copy.destination += &(tmp[8]);
955                 } else {
956                     local_copy.destination += path;
957                 }
958                 build_info.headers.push_back(local_copy);
959             }
960         }
961         CYG_REPORT_RETURN();
962         return;
963     }
964
965     // It is necessary to search for the appropriate files.
966     CdlInterpreter interp = loadable->get_interpreter();
967     std::string    path   = loadable->get_toplevel()->get_directory() + "/" + loadable->get_directory();
968     if (has_include_subdir) {
969         std::vector<std::string> files;
970         std::vector<std::string>::const_iterator file_i;
971         interp->locate_all_files(path + "/include", files);
972         for (file_i = files.begin(); file_i != files.end(); file_i++) {
973             // NOTE: for now discard any header files in the pkgconf subdirectory
974             if (0 == strncmp("pkgconf/", file_i->c_str(), 8)) {
975                 continue;
976             }
977             if ('~' == *(file_i->rbegin())) {
978                 continue;
979             }
980             CdlBuildInfo_Header local_copy;
981             local_copy.source   = "include/" + *file_i;
982             local_copy.destination      = "";
983             if ("" != dest_dir) {
984                 local_copy.destination = dest_dir + "/";
985             }
986             local_copy.destination += *file_i;
987             build_info.headers.push_back(local_copy);
988         }
989     } else {
990         // Look for all header files, which for now means files with
991         // a .h, .hxx, .inl or .inc extension.
992         // FIXME: the definition of what constitutes a header file
993         // should not be hard-wired here.
994         std::vector<std::string> files;
995         std::vector<std::string>::const_iterator file_i;
996         interp->locate_all_files(path, files);
997         for (file_i = files.begin(); file_i != files.end(); file_i++) {
998
999             // Problems with libstdc++ versions, use C comparisons instead.
1000             const char* c_string = file_i->c_str();
1001             unsigned int len = strlen(c_string);
1002             if (((len >= 2) && (0 == strncmp(c_string + len - 2, ".h", 2)))        ||
1003                 ((len >= 4) && (0 == strncmp(c_string + len - 4, ".hxx", 4)))      ||
1004                 ((len >= 4) && (0 == strncmp(c_string + len - 4, ".inl", 4)))      ||
1005                 ((len >= 4) && (0 == strncmp(c_string + len - 4, ".inc", 4)))) {
1006                 
1007                 CdlBuildInfo_Header local_copy;
1008                 local_copy.source = *file_i;
1009                 local_copy.destination = "";
1010                 if ("" != dest_dir) {
1011                     local_copy.destination = dest_dir + "/";
1012                 }
1013                 local_copy.destination += *file_i;
1014                 build_info.headers.push_back(local_copy);
1015             }
1016         }
1017     }
1018
1019     CYG_REPORT_RETURN();
1020     return;
1021 }
1022
1023 // ----------------------------------------------------------------------------
1024 // Updating a loadable build's info involves two steps. First, there
1025 // is some information associated with the loadable as a whole such as
1026 // header files. Second, each buildable in the loadable (including itself)
1027 // may contain properties such as compile etc. This is all handled via
1028 // a CdlBuildable member function.
1029
1030 void
1031 CdlBuildLoadableBody::update_build_info(CdlBuildInfo& build_info) const
1032 {
1033     CYG_REPORT_FUNCNAME("CdlBuildLoadable::update_build_info");
1034     CYG_REPORT_FUNCARG2XV(this, &build_info);
1035     CYG_PRECONDITION_THISC();
1036
1037     // It is not possible to disable a loadable itself: either the
1038     // loadable is present or it is not (although in some cases users
1039     // may be able to change versions). However, because of reparenting
1040     // it is possible for a loadable to be below a disabled container,
1041     // and hence it is still necessary to check whether or not the
1042     // loadable is active.
1043     if (!is_active()) {
1044         CYG_REPORT_RETURN();
1045         return;
1046     }
1047
1048     // Time to add a new CdlBuildInfo_Loadable object to the current
1049     // vector. The name and directory can be filled in straightaway,
1050     // the vectors will all be initialized to empty.
1051     CdlBuildInfo_Loadable tmp_info;
1052     build_info.entries.push_back(tmp_info);
1053     CdlBuildInfo_Loadable& this_info = *(build_info.entries.rbegin());
1054     this_info.name      = get_name();
1055     this_info.directory = get_directory();
1056
1057     // Take care of the header files
1058     update_header_file_info(this, this_info);
1059     
1060     // Work out the library name appropriate for this loadable.
1061     // There may be a library property, otherwise the global default
1062     // should be used.
1063     std::string loadable_library = default_library_name;
1064     if (has_property(CdlPropertyId_Library)) {
1065         CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(get_property(CdlPropertyId_Library));
1066         loadable_library = strprop->get_string();
1067     }
1068     
1069     const std::vector<CdlNode>& contents = get_owned();
1070     std::vector<CdlNode>::const_iterator node_i;
1071     for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
1072         CdlBuildable buildable = dynamic_cast<CdlBuildable>(*node_i);
1073         if (0 != buildable) {
1074             buildable->update_build_info(this_info, loadable_library);
1075         }
1076     }
1077     
1078     CYG_REPORT_RETURN();
1079 }
1080
1081 // This is much the same as the above, but there is no test for
1082 // active either at the loadable level or for the individual buildables.
1083 void
1084 CdlBuildLoadableBody::update_all_build_info(CdlBuildInfo& build_info) const
1085 {
1086     CYG_REPORT_FUNCNAME("CdlBuildLoadable::update_all_build_info");
1087     CYG_REPORT_FUNCARG2XV(this, &build_info);
1088     CYG_PRECONDITION_THISC();
1089
1090     CdlBuildInfo_Loadable tmp_info;
1091     build_info.entries.push_back(tmp_info);
1092     CdlBuildInfo_Loadable& this_info = *(build_info.entries.rbegin());
1093     this_info.name      = get_name();
1094     this_info.directory = get_directory();
1095
1096     std::string loadable_library = default_library_name;
1097     if (has_property(CdlPropertyId_Library)) {
1098         CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(get_property(CdlPropertyId_Library));
1099         loadable_library = strprop->get_string();
1100     }
1101     
1102     const std::vector<CdlNode>& contents = get_owned();
1103     std::vector<CdlNode>::const_iterator node_i;
1104     for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
1105         CdlBuildable buildable = dynamic_cast<CdlBuildable>(*node_i);
1106         if (0 != buildable) {
1107             buildable->update_build_info(this_info, loadable_library);
1108         }
1109     }
1110     
1111     CYG_REPORT_RETURN();
1112     CYG_REPORT_RETURN();
1113 }
1114
1115 //}}}
1116
1117 //}}}
1118 //{{{  Version number #define's         
1119
1120 // ----------------------------------------------------------------------------
1121 // Given a package xxxPKG_A_B_C with a version V1_2_3, generate additional
1122 // #define's of the form:
1123 //
1124 //   #define xxxNUM_A_B_C_VERSION_MAJOR 1
1125 //   #define xxxNUM_A_B_C_VERSION_MINOR 2
1126 //   #define xxxNUM_A_B_C_VERSION_RELEASE 3
1127 //
1128 // The goal here is to allow application code to cope with API
1129 // changes (which of course should be a rare event but cannot be
1130 // eliminated completely). C preprocessor #if statements are
1131 // essentially limited to numerical values, so there is no easy
1132 // way of coping with V1_2_3 at the preprocessor level. However it
1133 // is possible to cope with VERSION_NUMBER #define's.
1134 //
1135 // Note that only application code and third party packages are
1136 // affected. 
1137 //
1138 // These #define's go into system.h, alongside the main definition of
1139 // the package. There seems to be little point in putting them in the
1140 // package's own configuration header.
1141 //
1142 // There are three problems. First, what should be done for packages
1143 // which do not follow the naming conventions? Given a completely
1144 // random package rather than something like xxxPKG_..., what symbol
1145 // names should be used? Basically, if the package does not follow the
1146 // naming convention then there is no safe way of generating new
1147 // symbols. Any names that are chosen might clash. Of course even for
1148 // packages that do follow the naming convention a clash is still
1149 // possible, just a lot less likely.
1150 //
1151 // Conclusion: if a package does not follow the naming convention, do
1152 // not generate version #define's for it.
1153 //
1154 // Second, what happens if a different version numbering scheme is
1155 // used? For example the release number might be absent. Version
1156 // numbering schemes might change between releases, but application
1157 // code may still check the #define's.
1158 //
1159 // Third and related, what should happen for "current" and anoncvs? Do
1160 // we want to look at what other versions are installed and bump one
1161 // of the numbers?
1162 //
1163 // Conclusion: the version #define's always have to be generated,
1164 // even if they are not present in the version string, to allow
1165 // application code to test these symbols anyway. A safe default is
1166 // necessary, and -1 is probably the best bet. For example, if
1167 // the version is bumped from 1.3.287 to 1.4 then the release number
1168 // for the latter is set to -1. Another possible default would be
1169 // 0, but that could cause problems for packages that start counting
1170 // from 0 (not a common practice, but...)
1171 //
1172 // This leaves the question of what to do about "current". Chances are
1173 // that "current" comes from anoncvs and is always more recent than
1174 // any official release, so when comparing versions "current" should
1175 // always be greater than anything else. This can be achieved by using
1176 // a sufficiently large number for the major version. In practice
1177 // it is cleaner to have another #define to indicate the current
1178 // version, and then define package versions to match, i.e.:
1179 //
1180 //   #define CYGNUM_VERSION_CURRENT 0x7fffff00
1181 //   ...
1182 //   #define xxxNUM_A_B_C_VERSION_MAJOR   CYGNUM_VERSION_CURRENT
1183 //   #define xxxNUM_A_B_C_VERSION_MINOR   -1
1184 //   #define xxxNUM_A_B_C_VERSION_RELEASE -1
1185 //
1186 // All comparisons should now work sensibly. Leaving a little bit
1187 // of slack for VERSION_CURRENT seems like a good precaution.
1188
1189 static void
1190 system_h_add_version_header(Tcl_Channel system_h)
1191 {
1192     CYG_REPORT_FUNCNAME("system_h_add_version_header");
1193     Tcl_Write(system_h, "#define CYGNUM_VERSION_CURRENT 0x7fffff00\n", -1);
1194     CYG_REPORT_RETURN();
1195 }
1196
1197 static void
1198 system_h_add_package_versioning(Tcl_Channel system_h, std::string name, std::string value)
1199 {
1200     CYG_REPORT_FUNCNAME("system_h_add_package_versioning");
1201
1202     char name_buf[256];
1203     char line_buf[512];
1204     
1205     // The first thing to check is that the package name can be used
1206     // as the basis for the version symbols.
1207     bool ok = false;
1208     unsigned int i;
1209     for (i = 0; i < name.size(); i++) {
1210         if ('_' == name[i]) {
1211             if (3 < i) {
1212                 if ((name[i-3] == 'P') && (name[i-2] == 'K') && (name[i-1] == 'G')) {
1213                     ok = true;
1214                 }
1215             }
1216             break;
1217         }
1218     }
1219     if (name.size() >= 256) {
1220         ok = false;
1221     }
1222     if (!ok) {
1223         CYG_REPORT_RETURN();
1224         return;
1225     }
1226
1227     strcpy(name_buf, name.c_str());
1228     
1229     // Change from xxxPKG to xxxNUM
1230     name_buf[i - 3] = 'N';
1231     name_buf[i - 2] = 'U';
1232     name_buf[i - 1] = 'M';
1233
1234     // Now determine the version strings.
1235     std::string major   = "-1";
1236     std::string minor   = "-1";
1237     std::string release = "-1";
1238     if ("current" == value) {
1239         major   = "CYGNUM_VERSION_CURRENT";
1240     } else {
1241         Cdl::split_version_string(value, major, minor, release);
1242     }
1243
1244     sprintf(line_buf, "#define %s_VERSION_MAJOR %s\n", name_buf, major.c_str());
1245     Tcl_Write(system_h, line_buf, -1);
1246     sprintf(line_buf, "#define %s_VERSION_MINOR %s\n", name_buf, minor.c_str());
1247     Tcl_Write(system_h, line_buf, -1);
1248     sprintf(line_buf, "#define %s_VERSION_RELEASE %s\n", name_buf, release.c_str());
1249     Tcl_Write(system_h, line_buf, -1);
1250     
1251     CYG_REPORT_RETURN();
1252 }
1253
1254 //}}}
1255 //{{{  CdlDefinableBody                 
1256
1257 //{{{  Basics                                           
1258
1259 // ----------------------------------------------------------------------------
1260
1261 CdlDefinableBody::CdlDefinableBody()
1262 {
1263     CYG_REPORT_FUNCNAME("CdlDefinable:: default constructor");
1264     CYG_REPORT_FUNCARG1XV(this);
1265
1266     // There is no data to initialize
1267     cdldefinablebody_cookie = CdlDefinableBody_Magic;
1268     CYGDBG_MEMLEAK_CONSTRUCTOR();
1269     
1270     CYG_POSTCONDITION_THISC();
1271     CYG_REPORT_RETURN();
1272 }
1273
1274 CdlDefinableBody::~CdlDefinableBody()
1275 {
1276     CYG_REPORT_FUNCNAME("CdlDefinable:: destructor");
1277     CYG_REPORT_FUNCARG1XV(this);
1278     CYG_PRECONDITION_THISC();
1279
1280     cdldefinablebody_cookie = CdlDefinableBody_Invalid;
1281     CYGDBG_MEMLEAK_DESTRUCTOR();
1282     
1283     CYG_REPORT_RETURN();
1284 }
1285
1286 // ----------------------------------------------------------------------------
1287
1288 std::string
1289 CdlDefinableBody::get_class_name() const
1290 {
1291     CYG_REPORT_FUNCNAME("CdlDefinable::get_class_name");
1292     CYG_PRECONDITION_THISC();
1293     CYG_REPORT_RETURN();
1294     return "definable";
1295 }
1296
1297 // ----------------------------------------------------------------------------
1298
1299 bool
1300 CdlDefinableBody::check_this(cyg_assert_class_zeal zeal) const
1301 {
1302     if (CdlDefinableBody_Magic != cdldefinablebody_cookie) {
1303         return false;
1304     }
1305     CYGDBG_MEMLEAK_CHECKTHIS();
1306     return CdlNodeBody::check_this(zeal);
1307 }
1308
1309 //}}}
1310 //{{{  add_property_parser() and check_properties()     
1311
1312 // ----------------------------------------------------------------------------
1313
1314 void
1315 CdlDefinableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
1316 {
1317     CYG_REPORT_FUNCNAME("CdlDefinable::add_property_parsers");
1318
1319     static CdlInterpreterCommandEntry commands[] =
1320     {
1321         CdlInterpreterCommandEntry("no_define",          &parse_no_define     ),
1322         CdlInterpreterCommandEntry("define",             &parse_define        ),
1323         CdlInterpreterCommandEntry("define_format",      &parse_define_format ),
1324         CdlInterpreterCommandEntry("define_proc",        &parse_define_proc   ),
1325         CdlInterpreterCommandEntry("if_define",          &parse_if_define     ),
1326         CdlInterpreterCommandEntry("",                   0                    )
1327     };
1328
1329     for (int i = 0; commands[i].command != 0; i++) {
1330         std::vector<CdlInterpreterCommandEntry>::const_iterator j;
1331         for (j = parsers.begin(); j != parsers.end(); j++) {
1332             if (commands[i].name == j->name) {
1333                 if (commands[i].command != j->command) {
1334                     CYG_FAIL("Property names are being re-used");
1335                 }
1336                 break;
1337             }
1338         }
1339         if (j == parsers.end()) {
1340             parsers.push_back(commands[i]);
1341         }
1342     }
1343     CdlNodeBody::add_property_parsers(parsers);
1344     
1345     CYG_REPORT_RETURN();
1346 }
1347
1348 void
1349 CdlDefinableBody::check_properties(CdlInterpreter interp)
1350 {
1351     CYG_REPORT_FUNCNAME("CdlDefinable::check_properties");
1352     CYG_REPORT_FUNCARG2XV(this, interp);
1353     CYG_PRECONDITION_THISC();
1354     CYG_PRECONDITION_CLASSC(interp);
1355
1356     // There should be at most one each of no_define and define_format.
1357     if (count_properties(CdlPropertyId_NoDefine) > 1) {
1358         CdlParse::report_error(interp, "", "There should be at most one no_define property.");
1359     }
1360     if (count_properties(CdlPropertyId_DefineFormat) > 1) {
1361         CdlParse::report_error(interp, "", "There should be at most one define_format property.");
1362     }
1363     if (has_property(CdlPropertyId_NoDefine) && has_property(CdlPropertyId_DefineFormat)) {
1364         CdlParse::report_error(interp, "", "The no_define and define_format properties are mutually exclusive.");
1365     }
1366     // FIXME: the define_format property only makes sense for certain
1367     // flavors. However the flavor property may not have been processed yet.
1368     
1369     CdlNodeBody::check_properties(interp);
1370     
1371     CYG_REPORT_RETURN();
1372 }
1373
1374 //}}}
1375 //{{{  Definable properties                             
1376
1377 // ----------------------------------------------------------------------------
1378 // Syntax: no_define
1379 int
1380 CdlDefinableBody::parse_no_define(CdlInterpreter interp, int argc, const char* argv[])
1381 {
1382     CYG_REPORT_FUNCNAMETYPE("parse_no_define", "result %d");
1383
1384     int result = CdlParse::parse_minimal_property(interp, argc, argv, CdlPropertyId_NoDefine, 0, 0);
1385     
1386     CYG_REPORT_RETVAL(result);
1387     return result;
1388 }
1389
1390 // ----------------------------------------------------------------------------
1391 // syntax: define <symbol>
1392
1393 // The argument to "define" should be a valid C preprocessor symbol.
1394 static void
1395 parse_define_final_check(CdlInterpreter interp, CdlProperty_String prop)
1396 {
1397     CYG_REPORT_FUNCNAME("parse_define_final_check");
1398     CYG_PRECONDITION_CLASSC(prop);
1399     CYG_PRECONDITION_CLASSC(interp);
1400     
1401     const std::string& str = prop->get_string();
1402
1403     if (!Cdl::is_valid_c_preprocessor_symbol(str)) {
1404         CdlParse::report_property_parse_error(interp, prop, str + " is not a valid C preprocessor symbol");
1405     }
1406
1407     // There may be a file option. At this stage the only valid filename
1408     // that can be used here is system.h
1409     std::string file_option = prop->get_option("file");
1410     if (("" != file_option) && ("system.h" != file_option)) {
1411         CdlParse::report_property_parse_error(interp, prop, "Invalid -file option " + file_option);
1412     }
1413
1414     // FIXME: validate the format string
1415     
1416     CYG_REPORT_RETURN();
1417 }
1418
1419 int
1420 CdlDefinableBody::parse_define(CdlInterpreter interp, int argc, const char* argv[])
1421 {
1422     CYG_REPORT_FUNCNAMETYPE("parse_define", "result %d");
1423
1424     static char* options[] = {
1425         "file:",
1426         "format:",
1427         0
1428     };
1429     int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Define, options, &parse_define_final_check);
1430     
1431     CYG_REPORT_RETVAL(result);
1432     return result;
1433 }
1434
1435
1436 // ----------------------------------------------------------------------------
1437 // syntax: define_format <string>
1438 //
1439 // FIXME: it is possible to apply some checks to the string, e.g. that there
1440 // is only one conversion operation.
1441 //
1442 // FIXME: also check that the flavor is sensible, define_format has no effect
1443 // for none or bool
1444 //
1445 // FIXME: enforce mutual exclusion with no_define
1446
1447 int
1448 CdlDefinableBody::parse_define_format(CdlInterpreter interp, int argc, const char* argv[])
1449 {
1450     CYG_REPORT_FUNCNAMETYPE("parse_format", "result %d");
1451
1452     int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_DefineFormat, 0, 0);
1453     
1454     CYG_REPORT_RETVAL(result);
1455     return result;
1456 }
1457 // ----------------------------------------------------------------------------
1458 // syntax: define_proc <tclcode>
1459 int
1460 CdlDefinableBody::parse_define_proc(CdlInterpreter interp, int argc, const char* argv[])
1461 {
1462     CYG_REPORT_FUNCNAMETYPE("parse_define_proc", "result %d");
1463
1464     int result = CdlParse::parse_tclcode_property(interp, argc, argv, CdlPropertyId_DefineProc, 0, 0);
1465     
1466     CYG_REPORT_RETVAL(result);
1467     return result;
1468 }
1469
1470 // ----------------------------------------------------------------------------
1471 // Syntax: if_define sym1 sym2
1472
1473 static void
1474 parse_if_define_final_check(CdlInterpreter interp, CdlProperty_StringVector prop)
1475 {
1476     CYG_REPORT_FUNCNAME("parse_if_define_final_check");
1477     CYG_PRECONDITION_CLASSC(interp);
1478     CYG_PRECONDITION_CLASSC(prop);
1479     
1480     // There should be exactly two entries in the vector, and both of them should be
1481     // valid preprocessor symbols.
1482     const std::vector<std::string>& strings     = prop->get_strings();
1483
1484     if (2 != strings.size()) {
1485         CdlParse::report_property_parse_error(interp, prop, "There should be exactly two arguments.");
1486     }
1487     if (!Cdl::is_valid_c_preprocessor_symbol(strings[0])) {
1488         CdlParse::report_property_parse_error(interp, prop, strings[0] + " is not a valid C preprocessor symbol.");
1489     }
1490     if (!Cdl::is_valid_c_preprocessor_symbol(strings[1])) {
1491         CdlParse::report_property_parse_error(interp, prop, strings[1] + " is not a valid C preprocessor symbol.");
1492     }
1493     
1494     // There may be a file option. At this stage the only valid filename
1495     // that can be used here is system.h
1496     std::string file_option = prop->get_option("file");
1497     if (("" != file_option) && ("system.h" != file_option)) {
1498         CdlParse::report_property_parse_error(interp, prop, "Invalid -file option " + file_option);
1499     }
1500 }
1501
1502 int
1503 CdlDefinableBody::parse_if_define(CdlInterpreter interp, int argc, const char* argv[])
1504 {
1505     CYG_REPORT_FUNCNAMETYPE("parse_if_define", "result %d");
1506
1507     char* options[] = {
1508         "file:",
1509         0
1510     };
1511     int result = CdlParse::parse_stringvector_property(interp, argc, argv, CdlPropertyId_IfDefine, options,
1512                                                        &parse_if_define_final_check, false);
1513     
1514     CYG_REPORT_RETVAL(result);
1515     return result;
1516 }
1517
1518 //}}}
1519 //{{{  generate_config_header()                         
1520
1521 // ----------------------------------------------------------------------------
1522 // This code needs to allow for the following properties.
1523 //
1524 // 1) no_define. This suppresses the default #define generation. 
1525 //
1526 // 2) define_format <format_string.
1527 //
1528 // 3) define [-file <filename>][-format <format_string>] symbol
1529 //
1530 // 4) define_proc
1531 //
1532 // 5) if_define
1533
1534 void
1535 CdlDefinableBody::generate_config_header(Tcl_Channel this_hdr, Tcl_Channel system_h) const
1536 {
1537     CYG_REPORT_FUNCNAME("CdlDefinable::generate_config_header");
1538     CYG_REPORT_FUNCARG1XV(this);
1539     CYG_PRECONDITION_THISC();
1540
1541     CdlLoadable    loadable = get_owner();
1542     CdlInterpreter interp   = loadable->get_interpreter();
1543     
1544     // This definable is known to be active. However it may or may not be enabled.
1545     CYG_PRECONDITIONC(is_active());
1546
1547     std::string      name   = get_name();
1548     CdlValueFlavor   flavor = CdlValueFlavor_Bool;
1549     std::string      value  = "1";
1550     CdlConstValuable valuable        = dynamic_cast<CdlConstValuable>(this);
1551     if (0 != valuable) {
1552         // It is always possible to check the enabled() flag. 
1553         if (!valuable->is_enabled()) {
1554             CYG_REPORT_RETURN();
1555             return;
1556         }
1557         // The value is only valid for BoolData and Data flavors, and may
1558         // not have been provided. If there is no value then this option
1559         // should not generate a #define
1560         flavor = valuable->get_flavor();
1561         if ((CdlValueFlavor_BoolData == flavor) || (CdlValueFlavor_Data == flavor)) {
1562             value = valuable->get_value();
1563         }
1564     }
1565
1566     // Flavor and value are now both set to sensible strings.
1567     // First, check the no_define property. If this is present then the default
1568     // #define generation should be suppressed.
1569     if (!has_property(CdlPropertyId_NoDefine)) {
1570
1571         // OK, it is necessary to generate at least one #define.
1572         // If this node is actually a loadable then the #define should go
1573         // into system.h, otherwise into the current header
1574         Tcl_Channel chan = this_hdr;
1575         if (dynamic_cast<CdlConstLoadable>((CdlConstNode)this) == loadable) {
1576             chan = system_h;
1577         }
1578
1579         // For flavors None and Bool, there should be just one #define
1580         if ((CdlValueFlavor_None == flavor) || (CdlValueFlavor_Bool == flavor)) {
1581             std::string define = "#define " + name + " 1\n";
1582             Tcl_Write(chan, const_cast<char*>(define.c_str()), -1);
1583         } else {
1584             // If there is a format string then that controls the default
1585             // value display.
1586             if (!has_property(CdlPropertyId_DefineFormat)) {
1587                 std::string define = "#define " + name + " " + value + "\n";
1588                 Tcl_Write(chan, const_cast<char*>(define.c_str()), -1);
1589             } else {
1590                 CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(get_property(CdlPropertyId_DefineFormat));
1591                 CYG_ASSERT_CLASSC(strprop);
1592                 std::string format = strprop->get_string();
1593                 std::string cmd = "return \"#define " + name + " [format " + format + " " + value + "]\n\"";
1594                 std::string define;
1595                 if (TCL_OK != interp->eval(cmd, define)) {
1596                     throw CdlInputOutputException("Internal error executing tcl fragment to process define_format property");
1597                 }
1598                 Tcl_Write(chan, const_cast<char*>(define.c_str()), -1);
1599             }
1600
1601             // There may also be a separate #define of the form <name>_<value>,
1602             // if that is a valid preprocessor symbol.
1603             std::string tmp = name + "_" + value;
1604             if (Cdl::is_valid_c_preprocessor_symbol(tmp)) {
1605                 tmp = "#define "+ tmp + "\n";
1606                 Tcl_Write(chan, const_cast<char*>(tmp.c_str()), -1);
1607             }
1608             
1609             // For loadables, add additional version information to system_h
1610             if (dynamic_cast<CdlConstLoadable>((CdlConstNode)this) == loadable) {
1611                 system_h_add_package_versioning(system_h, name, value);
1612             }
1613         }
1614     }
1615
1616     // Next, check for any additional define properties
1617     std::vector<CdlProperty> define_props;
1618     get_properties(CdlPropertyId_Define, define_props);
1619     std::vector<CdlProperty>::const_iterator prop_i;
1620     for (prop_i = define_props.begin(); prop_i != define_props.end(); prop_i++) {
1621         CdlProperty_String strprop = dynamic_cast<CdlProperty_String>(*prop_i);
1622         CYG_ASSERT_CLASSC(strprop);
1623         std::string symbol = strprop->get_string();
1624
1625         std::string file = strprop->get_option("file");
1626         Tcl_Channel chan = this_hdr;
1627         if (("" != file) && ("system.h" == file)) {
1628             chan = system_h;
1629         }
1630
1631         if ((CdlValueFlavor_None == flavor) || (CdlValueFlavor_Bool == flavor)) {
1632             std::string define = "#define " + symbol + " 1\n";
1633             Tcl_Write(chan, const_cast<char*>(define.c_str()), -1);
1634         } else {
1635             std::string format = strprop->get_option("format");
1636             if ("" == format) {
1637                 std::string define = "#define " + symbol + " " + value + "\n";
1638                 Tcl_Write(chan, const_cast<char*>(define.c_str()), -1);
1639             } else {
1640                 std::string cmd = "return \"#define " + symbol + " [format " + format + " " + value + "]\n\"";
1641                 std::string define;
1642                 if (TCL_OK != interp->eval(cmd, define)) {
1643                     throw CdlInputOutputException("Internal error executing tcl fragment to process format option");
1644                 }
1645                 Tcl_Write(chan, const_cast<char*>(define.c_str()), -1);
1646             }
1647
1648             std::string tmp = symbol + "_" + value;
1649             if (Cdl::is_valid_c_preprocessor_symbol(tmp)) {
1650                 tmp = "#define " + tmp + "\n";
1651                 Tcl_Write(chan, const_cast<char*>(tmp.c_str()), -1);
1652             }
1653         }
1654     }
1655
1656     // Now check for if_define properties
1657     std::vector<CdlProperty> if_define_props;
1658     get_properties(CdlPropertyId_IfDefine, if_define_props);
1659     for (prop_i = if_define_props.begin(); prop_i != if_define_props.end(); prop_i++) {
1660         CdlProperty_StringVector strprop = dynamic_cast<CdlProperty_StringVector>(*prop_i);
1661         CYG_ASSERT_CLASSC(strprop);
1662         CYG_ASSERTC(2 == strprop->get_number_of_strings());
1663
1664         std::string sym1 = strprop->get_string(0);
1665         std::string sym2 = strprop->get_string(1);
1666
1667         Tcl_Channel chan = this_hdr;
1668         std::string file = strprop->get_option("file");
1669         if (("" != file) && ("system.h" == file)) {
1670             chan = system_h;
1671         }
1672         std::string data = "#ifdef " + sym1 + "\n# define " + sym2 + " 1\n#endif\n";
1673         Tcl_Write(chan, const_cast<char*>(data.c_str()), -1);
1674     }
1675
1676     // And define_proc properties
1677     std::vector<CdlProperty> define_proc_props;
1678     get_properties(CdlPropertyId_DefineProc, define_proc_props);
1679     for (prop_i = define_proc_props.begin(); prop_i != define_proc_props.end(); prop_i++) {
1680         CdlProperty_TclCode codeprop = dynamic_cast<CdlProperty_TclCode>(*prop_i);
1681         CYG_ASSERT_CLASSC(codeprop);
1682
1683         cdl_tcl_code code       = codeprop->get_code();
1684         std::string  result;
1685         if (TCL_OK != interp->eval(code, result)) {
1686             throw CdlInputOutputException("Error evaluating define_proc property for " + name + "\n" + result);
1687         }
1688     }
1689     
1690     
1691     CYG_REPORT_RETURN();
1692 }
1693
1694 //}}}
1695
1696 //}}}
1697 //{{{  CdlDefineLoadableBody            
1698
1699 //{{{  Basics                           
1700
1701 // ----------------------------------------------------------------------------
1702
1703 CdlDefineLoadableBody::CdlDefineLoadableBody()
1704 {
1705     CYG_REPORT_FUNCNAME("CdlDefineLoadable:: default constructor");
1706     CYG_REPORT_FUNCARG1XV(this);
1707
1708     cdldefineloadablebody_cookie = CdlDefineLoadableBody_Magic;
1709     CYGDBG_MEMLEAK_CONSTRUCTOR();
1710     
1711     CYG_POSTCONDITION_THISC();
1712     CYG_REPORT_RETURN();
1713 }
1714
1715 CdlDefineLoadableBody::~CdlDefineLoadableBody()
1716 {
1717     CYG_REPORT_FUNCNAME("CdlDefineLoadable:: destructor");
1718     CYG_REPORT_FUNCARG1XV(this);
1719     CYG_PRECONDITION_THISC();
1720
1721     cdldefineloadablebody_cookie = CdlDefineLoadableBody_Invalid;
1722     CYGDBG_MEMLEAK_DESTRUCTOR();
1723     
1724     CYG_REPORT_RETURN();
1725 }
1726
1727 // ----------------------------------------------------------------------------
1728
1729 std::string
1730 CdlDefineLoadableBody::get_class_name() const
1731 {
1732     CYG_REPORT_FUNCNAME("CdlDefineLoadable::get_class_name");
1733     CYG_PRECONDITION_THISC();
1734     CYG_REPORT_RETURN();
1735     return "define_loadable";
1736 }
1737
1738 // ----------------------------------------------------------------------------
1739
1740 bool
1741 CdlDefineLoadableBody::check_this(cyg_assert_class_zeal zeal) const
1742 {
1743     if (CdlDefineLoadableBody_Magic != cdldefineloadablebody_cookie) {
1744         return false;
1745     }
1746     CYGDBG_MEMLEAK_CHECKTHIS();
1747     return CdlLoadableBody::check_this(zeal) && CdlNodeBody::check_this(zeal);
1748 }
1749
1750 //}}}
1751 //{{{  Property parsing                 
1752
1753 // ----------------------------------------------------------------------------
1754
1755 void
1756 CdlDefineLoadableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
1757 {
1758     CYG_REPORT_FUNCNAME("CdlDefineLoadable::add_property_parsers");
1759
1760     static CdlInterpreterCommandEntry commands[] =
1761     {
1762         CdlInterpreterCommandEntry("define_header",      &parse_define_header),
1763         CdlInterpreterCommandEntry("",                   0                   )
1764     };
1765
1766     for (int i = 0; commands[i].command != 0; i++) {
1767         std::vector<CdlInterpreterCommandEntry>::const_iterator j;
1768         for (j = parsers.begin(); j != parsers.end(); j++) {
1769             if (commands[i].name == j->name) {
1770                 if (commands[i].command != j->command) {
1771                     CYG_FAIL("Property names are being re-used");
1772                 }
1773                 break;
1774             }
1775         }
1776         if (j == parsers.end()) {
1777             parsers.push_back(commands[i]);
1778         }
1779     }
1780     CdlNodeBody::add_property_parsers(parsers);
1781     
1782     CYG_REPORT_RETURN();
1783 }
1784
1785 void
1786 CdlDefineLoadableBody::check_properties(CdlInterpreter interp)
1787 {
1788     CYG_REPORT_FUNCNAME("CdlDefineLoadable::check_properties");
1789     CYG_REPORT_FUNCARG2XV(this, interp);
1790     CYG_PRECONDITION_THISC();
1791     CYG_PRECONDITION_CLASSC(interp);
1792
1793     // There should be at most one define_header property
1794     int count = count_properties(CdlPropertyId_DefineHeader);
1795     if (count> 1) {
1796         CdlParse::report_error(interp, "", "There should be at most one define_header property.");
1797     }
1798     // FIXME: filename validation
1799     
1800     CdlNodeBody::check_properties(interp);
1801     
1802     CYG_REPORT_RETURN();
1803 }
1804
1805 // ----------------------------------------------------------------------------
1806 // syntax: define_header <header file name>
1807 int
1808 CdlDefineLoadableBody::parse_define_header(CdlInterpreter interp, int argc, const char* argv[])
1809 {
1810     CYG_REPORT_FUNCNAMETYPE("parse_define_header", "result %d");
1811
1812     int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_DefineHeader, 0, 0);
1813     
1814     CYG_REPORT_RETVAL(result);
1815     return result;
1816 }
1817
1818 //}}}
1819 //{{{  generate_config_header()         
1820
1821 // ----------------------------------------------------------------------------
1822 void
1823 CdlDefineLoadableBody::generate_config_header(Tcl_Channel this_hdr, Tcl_Channel system_h) const
1824 {
1825     CYG_REPORT_FUNCNAME("CdlDefineLoadable::generate_config_header");
1826     CYG_REPORT_FUNCARG1XV(this);
1827     CYG_PRECONDITION_THISC();
1828
1829     CdlInterpreter interp       = get_interpreter();
1830     Tcl_RegisterChannel(interp->get_tcl_interpreter(), this_hdr);
1831     Tcl_RegisterChannel(interp->get_tcl_interpreter(), system_h);
1832
1833     CdlInterpreterBody::ContextSupport(interp, std::string("Package ") + this->get_name() + ", header file generation");
1834     
1835     try {
1836         interp->set_variable("::cdl_header", Tcl_GetChannelName(this_hdr));
1837         interp->set_variable("::cdl_system_header", Tcl_GetChannelName(system_h));
1838
1839         const std::vector<CdlNode>& contents = get_owned();
1840         std::vector<CdlNode>::const_iterator node_i;
1841         for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
1842             CdlDefinable definable = dynamic_cast<CdlDefinable>(*node_i);
1843             if (0 == definable) {
1844                 continue;
1845             }
1846             if (!definable->is_active()) {
1847                 continue;
1848             }
1849             definable->generate_config_header(this_hdr, system_h);
1850         }
1851     
1852         interp->unset_variable("::cdl_header");
1853         interp->unset_variable("::cdl_system_header");
1854     } catch(...) {
1855         Tcl_UnregisterChannel(interp->get_tcl_interpreter(), this_hdr);
1856         Tcl_UnregisterChannel(interp->get_tcl_interpreter(), system_h);
1857         throw;
1858     }
1859     
1860     Tcl_UnregisterChannel(interp->get_tcl_interpreter(), this_hdr);
1861     Tcl_UnregisterChannel(interp->get_tcl_interpreter(), system_h);
1862     
1863     CYG_REPORT_RETURN();
1864 }
1865
1866 //}}}
1867 //{{{  get_config_headers()             
1868
1869 // ----------------------------------------------------------------------------
1870 // What header file should be generated for this loadable?
1871 //
1872 // If there is a define_header property then this should be used.
1873 // Otherwise a filename is constructed from the loadable's name.
1874
1875 std::string
1876 CdlDefineLoadableBody::get_config_header() const
1877 {
1878     CYG_REPORT_FUNCNAME("CdlDefineLoadable::get_config_headers");
1879     CYG_REPORT_FUNCARG1XV(this);
1880     CYG_PRECONDITION_THISC();
1881
1882     std::string result = "";
1883     CdlProperty prop = get_property(CdlPropertyId_DefineHeader);
1884     if (0 != prop) {
1885         CdlProperty_String string_prop = dynamic_cast<CdlProperty_String>(prop);
1886         CYG_ASSERT_CLASSC(string_prop);
1887         result = string_prop->get_string();
1888     } else {
1889         std::string tmp = get_name();
1890         result = Cdl::get_short_form(tmp);
1891         result += ".h";
1892     }
1893     CYG_REPORT_RETURN();
1894     return result;
1895 }
1896
1897 //}}}
1898
1899 //}}}
1900 //{{{  CdlToplevel                      
1901
1902 //{{{  CdlToplevel::get_build_info()            
1903
1904 // ----------------------------------------------------------------------------
1905 // Essentially this code involves iterating over the loadables vector,
1906 // looking for BuildLoadables and invoking their update_build_info()
1907 // member function. In addition, if there is currently some data in
1908 // the build_info vector (probably from a previous call) then that
1909 // must be cleared.
1910
1911 void
1912 CdlToplevelBody::get_build_info(CdlBuildInfo& build_info)
1913 {
1914     CYG_REPORT_FUNCNAME("CdlToplevel::get_build_info");
1915     CYG_REPORT_FUNCARG2XV(this, &build_info);
1916     CYG_PRECONDITION_THISC();
1917
1918     if (0 != build_info.entries.size()) {
1919         build_info.entries.clear();
1920     }
1921
1922     const std::vector<CdlLoadable>&          loadables   = get_loadables();
1923     std::vector<CdlLoadable>::const_iterator load_i;
1924     for (load_i = loadables.begin(); load_i != loadables.end(); load_i++) {
1925         CdlConstBuildLoadable bl = dynamic_cast<CdlConstBuildLoadable>(*load_i);
1926         if (0 != bl) {
1927             bl->update_build_info(build_info);
1928         }
1929     }
1930
1931     CYG_REPORT_RETURN();
1932 }
1933
1934 //}}}
1935 //{{{  CdlToplevel::get_all_build_info()        
1936
1937 // ----------------------------------------------------------------------------
1938 // This is just like get_build_info(), but calls a different
1939 // BuildLoadable member.
1940
1941 void
1942 CdlToplevelBody::get_all_build_info(CdlBuildInfo& build_info)
1943 {
1944     CYG_REPORT_FUNCNAME("CdlToplevel::get_all_build_info");
1945     CYG_REPORT_FUNCARG2XV(this, &build_info);
1946     CYG_PRECONDITION_THISC();
1947
1948     if (0 != build_info.entries.size()) {
1949         build_info.entries.clear();
1950     }
1951
1952     const std::vector<CdlLoadable>&          loadables   = get_loadables();
1953     std::vector<CdlLoadable>::const_iterator load_i;
1954     for (load_i = loadables.begin(); load_i != loadables.end(); load_i++) {
1955         CdlConstBuildLoadable bl = dynamic_cast<CdlConstBuildLoadable>(*load_i);
1956         if (0 != bl) {
1957             bl->update_all_build_info(build_info);
1958         }
1959     }
1960
1961     CYG_REPORT_RETURN();
1962 }
1963
1964 //}}}
1965 //{{{  CdlToplevel::generate_config_headers()   
1966
1967 // ----------------------------------------------------------------------------
1968 // Generating the config headers. This involves the following steps:
1969 //
1970 // 1) for every DefineLoadable, find out what header file should
1971 //    be generated. Note that some loadables may share a header file.
1972 //
1973 // 2) create a temporary version of system.h. Note that it is not
1974 //    a good idea to just overwrite an existing system.h, chances
1975 //    are pretty good that the file will not have changed, and
1976 //    updating it unnecessarily will result in unnecessary rebuilds
1977 //    due to header file dependencies.
1978 //
1979 // 3) for each file that should be generated, create a temporary
1980 //    version and allow all applicable loadables to update it.
1981
1982 // A utility to compare two files and do the right thing.
1983 // This requires some simple Tcl code.
1984 static void
1985 compare_and_copy(CdlInterpreter interp, std::string file1, std::string file2)
1986 {
1987     CYG_REPORT_FUNCNAME("compare_and_copy");
1988     CYG_PRECONDITION_CLASSC(interp);
1989     CYG_PRECONDITIONC("" != file1);
1990     CYG_PRECONDITIONC("" != file2);
1991     CYG_PRECONDITIONC(file1 != file2);
1992
1993     static char compare_and_copy_script[] = "\
1994 if {[file exists \"$::cdl_compare_and_copy_file2\"] == 0} {                                   \n\
1995     catch { file rename -- $::cdl_compare_and_copy_file1 $::cdl_compare_and_copy_file2}       \n\
1996     return                                                                                    \n\
1997 }                                                                                             \n\
1998 set fd [open \"$::cdl_compare_and_copy_file1\" r]                                             \n\
1999 set data1 [read $fd]                                                                          \n\
2000 close $fd                                                                                     \n\
2001 set fd [open \"$::cdl_compare_and_copy_file2\" r]                                             \n\
2002 set data2 [read $fd]                                                                          \n\
2003 close $fd                                                                                     \n\
2004 if {$data1 == $data2} {                                                                       \n\
2005     file delete \"$::cdl_compare_and_copy_file1\"                                             \n\
2006 } else {                                                                                      \n\
2007     catch { file rename -force -- $::cdl_compare_and_copy_file1 $::cdl_compare_and_copy_file2 } \n\
2008 }                                                                                             \n\
2009 ";
2010
2011     interp->set_variable("::cdl_compare_and_copy_file1", file1);
2012     interp->set_variable("::cdl_compare_and_copy_file2", file2);
2013     std::string tcl_result;
2014     if (TCL_OK != interp->eval(compare_and_copy_script, tcl_result)) {
2015         throw CdlInputOutputException("internal error manipulating temporary header " + file1 + " and target " + file2 +
2016             "\n" + tcl_result);
2017     }
2018 }
2019
2020 void
2021 CdlToplevelBody::generate_config_headers(std::string directory)
2022 {
2023     CYG_REPORT_FUNCNAME("CdlToplevel::generate_config_headers");
2024     CYG_REPORT_FUNCARG1XV(this);
2025     CYG_PRECONDITION_THISC();
2026     CYG_ASSERTC("" != directory);
2027
2028     // Replace any backslashes in the path with forward slashes. The
2029     // latter are used throughout the library
2030     // NOTE: this is not i18n-friendly.
2031     for (unsigned int i = 0; i < directory.size(); i++) {
2032         if ('\\' == directory[i]) {
2033             directory[i] = '/';
2034         }
2035     }
2036     
2037     CdlInterpreter interp = get_interpreter();
2038     std::string    tcl_result;
2039     if ((TCL_OK != interp->eval("file isdirectory \"" + directory + "\"", tcl_result)) ||
2040         (tcl_result != "1")) {
2041         throw CdlInputOutputException("target " + directory + " is not a valid existing directory.");
2042     }
2043     
2044     std::vector<std::pair<CdlDefineLoadable, std::string> > headers;
2045     const std::vector<CdlLoadable>& loadables = get_loadables();
2046     std::vector<CdlLoadable>::const_iterator load_i;
2047     for (load_i = loadables.begin(); load_i != loadables.end(); load_i++) {
2048         CdlDefineLoadable tmp = dynamic_cast<CdlDefineLoadable>(*load_i);
2049         if (0 != tmp) {
2050             std::string hdr = tmp->get_config_header();
2051             headers.push_back(std::make_pair(tmp, hdr));
2052         }
2053     }
2054
2055     static char banner_format[] =
2056 "#ifndef CYGONCE_PKGCONF_%s\n\
2057 #define CYGONCE_PKGCONF_%s\n\
2058 /*\n\
2059  * File <pkgconf/%s>\n\
2060  *\n\
2061  * This file is generated automatically by the configuration\n\
2062  * system. It should not be edited. Any changes to this file\n\
2063  * may be overwritten.\n\
2064  */\n\
2065 \n";
2066 #ifdef _WIN32
2067     // Create three channels which Tcl will use for standard streams
2068     // if these streams do not already exist. This avoids a Tcl
2069     // problem which can prevent closure of system.h. (FIXME)
2070     Tcl_Channel stdin_chan = Tcl_OpenFileChannel(interp->get_tcl_interpreter(), "nul", "w", 0666);
2071     Tcl_Channel stdout_chan = Tcl_OpenFileChannel(interp->get_tcl_interpreter(), "nul", "w", 0666);
2072     Tcl_Channel stderr_chan = Tcl_OpenFileChannel(interp->get_tcl_interpreter(), "nul", "w", 0666);
2073     Tcl_RegisterChannel(0, stdin_chan);
2074     Tcl_RegisterChannel(0, stdout_chan);
2075     Tcl_RegisterChannel(0, stderr_chan);
2076 #endif
2077     // Assume for now that files __libcdl_file1 and __libcdl_file2 are
2078     // legal on all platforms of interest, and that nobody is going to
2079     // export to these.
2080     std::string system_h_name = directory + "/__libcdl_file1";
2081     Tcl_Channel system_h = Tcl_OpenFileChannel(interp->get_tcl_interpreter(),
2082                                                const_cast<char*>(system_h_name.c_str()), "w", 0666);
2083     if (0 == system_h) {
2084         throw CdlInputOutputException("Unable to open file " + system_h_name + "\n" +
2085                                       interp->get_result());
2086     }
2087     // The channel will be registered and unregistered in several
2088     // different interpreters. This call prevents the channel from
2089     // disappearing prematurely.
2090     Tcl_RegisterChannel(0, system_h);
2091
2092     // Make sure that this operation is undone if necessary.
2093     try {
2094         // Now fill in system.h with the appropriate data. Start with the banner.
2095         char local_buf[512];
2096         sprintf(local_buf, banner_format, "SYSTEM_H", "SYSTEM_H", "system.h");
2097         Tcl_Write(system_h, local_buf, -1);
2098
2099         // Add generic version information
2100         system_h_add_version_header(system_h);
2101         
2102         // The rest of system.h will be filled in by the following loop.
2103         //
2104         // Walk down the previously constructed headers vector, create
2105         // appropriate files, and let each DefineLoadable fill in the
2106         // file for itself.
2107         std::vector<std::pair<CdlDefineLoadable, std::string> >::iterator outer_i;
2108         std::vector<std::pair<CdlDefineLoadable, std::string> >::iterator inner_i;
2109         for (outer_i = headers.begin(); outer_i != headers.end(); outer_i++) {
2110             if ("" == outer_i->second) {
2111                 continue;
2112             }
2113             std::string target_name = outer_i->second;
2114             std::string header_name = directory + "/__libcdl_file2";
2115             Tcl_Channel header_h = Tcl_OpenFileChannel(interp->get_tcl_interpreter(),
2116                                                        const_cast<char*>(header_name.c_str()), "w", 0666);
2117             if (0 == header_h) {
2118                 throw CdlInputOutputException("Unable to open file " + header_name + "\n" +
2119                                               interp->get_result());
2120             }
2121             // The channel may be used in several different interpreters, so
2122             // do an extra register operation
2123             Tcl_RegisterChannel(0, header_h);
2124
2125             try {
2126                 // Output the banner. This requires an all-upper-case version of the
2127                 // header name.
2128                 std::string upper_case;
2129                 for (unsigned int i = 0; i < target_name.size(); i++) {
2130                     if (islower(target_name[i])) {
2131                         upper_case += toupper(target_name[i]);
2132                     } else if ('.' == target_name[i]) {
2133                         upper_case += '_';
2134                     } else {
2135                         upper_case += target_name[i];
2136                     }
2137                 }
2138                 sprintf(local_buf, banner_format, upper_case.c_str(), upper_case.c_str(), target_name.c_str());
2139                 Tcl_Write(header_h, local_buf, -1);
2140
2141                 // Now iterate over all the loadables looking for ones which
2142                 // should generate #define's for this header, and invoke the
2143                 // appropriate member function.
2144                 for (inner_i = outer_i; inner_i != headers.end(); inner_i++) {
2145                     if (inner_i->second == target_name) {
2146                         inner_i->first->generate_config_header(header_h, system_h);
2147                         inner_i->second = "";
2148                     }
2149                 }
2150         
2151                 // The header file has now been updated. Close it and decide whether
2152                 // or not to replace the old version
2153                 Tcl_Write(header_h, "\n#endif\n", -1);
2154             } catch(...) {
2155                 Tcl_UnregisterChannel(0, header_h);
2156                 throw;
2157             }
2158             Tcl_UnregisterChannel(0, header_h);
2159             compare_and_copy(interp, header_name, directory + "/" + target_name);
2160         }
2161         
2162         Tcl_Write(system_h, "\n#endif\n", -1);
2163     } catch(...) {
2164         Tcl_UnregisterChannel(0, system_h);
2165         throw;
2166     }
2167     
2168     // This call to UnregisterChannel automatically closes the
2169     // channel, there is no need for an explicit Tcl_Close() call.
2170     Tcl_UnregisterChannel(0, system_h);
2171 #ifdef _WIN32
2172     Tcl_UnregisterChannel(0, stderr_chan);
2173     Tcl_UnregisterChannel(0, stdout_chan);
2174     Tcl_UnregisterChannel(0, stdin_chan);
2175 #endif
2176     compare_and_copy(interp, system_h_name, directory +"/system.h");
2177 }
2178
2179 //}}}
2180 //{{{  CdlToplevel::get_config_headers()        
2181
2182 // ----------------------------------------------------------------------------
2183 // Return details of the header files that should be generated. This
2184 // allows higher-level code to detect files that should no longer
2185 // be present, amongst other uses.
2186 //
2187 // The main complication is that some packages may wish to share the
2188 // same header file, especially hardware packages.
2189
2190 void
2191 CdlToplevelBody::get_config_headers(std::vector<std::string>& headers)
2192 {
2193     CYG_REPORT_FUNCNAME("CdlToplevelBody::get_config_headers");
2194     CYG_REPORT_FUNCARG2XV(this, &headers);
2195     CYG_PRECONDITION_THISC();
2196
2197     // Allow the vector argument to be re-used in multiple calls.
2198     // Strictly speaking this is better done at the application
2199     // level, but the behaviour is consistent with get_build_info();
2200     headers.clear();
2201
2202     // There will always be a system.h header file with details
2203     // of the loadables.
2204     // FIXME: the name of this file should probably be controllable
2205     headers.push_back("system.h");
2206
2207     // Now check each loadable and adds its header file, assuming
2208     // this is unique.
2209     const std::vector<CdlLoadable>& loadables = get_loadables();
2210     std::vector<CdlLoadable>::const_iterator i;
2211     for (i = loadables.begin(); i != loadables.end(); i++) {
2212         CdlDefineLoadable current = dynamic_cast<CdlDefineLoadable>(*i);
2213         if (0 != current) {
2214             std::string its_file = current->get_config_header();
2215             CYG_LOOP_INVARIANTC("" != its_file);
2216             if (std::find(headers.begin(), headers.end(), its_file) == headers.end()) {
2217                 headers.push_back(its_file);
2218             }
2219         }
2220     }
2221
2222     CYG_REPORT_RETURN();
2223 }
2224
2225 //}}}
2226 //{{{  CdlToplevel::generate_build_tree()       
2227
2228 void
2229 CdlToplevelBody::generate_build_tree(std::string build_tree, std::string install_tree)
2230 {
2231 }
2232
2233 //}}}
2234
2235 //}}}