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