OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [host/] [libcdl/] [build.cxx] - Blame information for rev 790

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 786 skrzyp
//{{{  Banner                           
2
 
3
//==========================================================================
4
//
5
//      build.cxx
6
//
7
//      libcdl support for building and for header file generation
8
//
9
//==========================================================================
10
// ####ECOSHOSTGPLCOPYRIGHTBEGIN####                                        
11
// -------------------------------------------                              
12
// This file is part of the eCos host tools.                                
13
// Copyright (C) 1999, 2000, 2001, 2002, 2003, 2008 Free Software Foundation, Inc.
14
//
15
// This program is free software; you can redistribute it and/or modify     
16
// it under the terms of the GNU General Public License as published by     
17
// the Free Software Foundation; either version 2 or (at your option) any   
18
// later version.                                                           
19
//
20
// This program is distributed in the hope that it will be useful, but      
21
// WITHOUT ANY WARRANTY; without even the implied warranty of               
22
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        
23
// General Public License for more details.                                 
24
//
25
// You should have received a copy of the GNU General Public License        
26
// along with this program; if not, write to the                            
27
// Free Software Foundation, Inc., 51 Franklin Street,                      
28
// Fifth Floor, Boston, MA  02110-1301, USA.                                
29
// -------------------------------------------                              
30
// ####ECOSHOSTGPLCOPYRIGHTEND####                                          
31
//==========================================================================
32
//#####DESCRIPTIONBEGIN####                                             
33
//
34
// Author(s):           bartv
35
// Contributors:        bartv
36
// Date:                1999-06-018
37
//
38
//####DESCRIPTIONEND####
39
//==========================================================================
40
 
41
//}}}
42
//{{{  #include's                       
43
 
44
// ----------------------------------------------------------------------------
45
#include "cdlconfig.h"
46
 
47
#include <cstring>
48
 
49
// Get the infrastructure types, assertions, tracing and similar
50
// facilities.
51
#include <cyg/infra/cyg_ass.h>
52
#include <cyg/infra/cyg_trac.h>
53
 
54
// <cdl.hxx> defines everything implemented in this module.
55
// It implicitly supplies <string>, <vector> and <map> because
56
// the class definitions rely on these headers.
57
#include <cdlcore.hxx>
58
 
59
//}}}
60
 
61
//{{{  Statics                          
62
 
63
// ----------------------------------------------------------------------------
64
CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlBuildLoadableBody);
65
CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlBuildableBody);
66
CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlDefineLoadableBody);
67
CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlDefinableBody);
68
 
69
//}}}
70
//{{{  CdlBuildableBody                 
71
 
72
//{{{  Basics                           
73
 
74
// ----------------------------------------------------------------------------
75
// There is little data specific to a buildable. The only distinguishing
76
// feature is the set of properties that are supported, plus a handful
77
// of functions to extract that information.
78
 
79
CdlBuildableBody::CdlBuildableBody()
80
{
81
    CYG_REPORT_FUNCNAME("CdlBuildable:: default constructor");
82
    CYG_REPORT_FUNCARG1XV(this);
83
 
84
    // There is no data to initialize yet
85
    cdlbuildablebody_cookie = CdlBuildableBody_Magic;
86
    CYGDBG_MEMLEAK_CONSTRUCTOR();
87
 
88
    CYG_POSTCONDITION_THISC();
89
    CYG_REPORT_RETURN();
90
}
91
 
92
CdlBuildableBody::~CdlBuildableBody()
93
{
94
    CYG_REPORT_FUNCNAME("CdlBuildable:: destructor");
95
    CYG_REPORT_FUNCARG1XV(this);
96
    CYG_PRECONDITION_THISC();
97
 
98
    cdlbuildablebody_cookie = CdlBuildableBody_Invalid;
99
    CYGDBG_MEMLEAK_DESTRUCTOR();
100
 
101
    CYG_REPORT_RETURN();
102
}
103
 
104
// ----------------------------------------------------------------------------
105
 
106
std::string
107
CdlBuildableBody::get_class_name() const
108
{
109
    CYG_REPORT_FUNCNAME("CdlBuildable::get_class_name");
110
    CYG_PRECONDITION_THISC();
111
    CYG_REPORT_RETURN();
112
    return "buildable";
113
}
114
 
115
// ----------------------------------------------------------------------------
116
 
117
bool
118
CdlBuildableBody::check_this(cyg_assert_class_zeal zeal) const
119
{
120
    if (CdlBuildableBody_Magic != cdlbuildablebody_cookie) {
121
        return false;
122
    }
123
    CYGDBG_MEMLEAK_CHECKTHIS();
124
    return CdlNodeBody::check_this(zeal);
125
}
126
 
127
//}}}
128
//{{{  Add and check property parsers   
129
 
130
// ----------------------------------------------------------------------------
131
 
132
void
133
CdlBuildableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
134
{
135
    CYG_REPORT_FUNCNAME("CdlBuildable::add_property_parsers");
136
 
137
    static CdlInterpreterCommandEntry commands[] =
138
    {
139
        CdlInterpreterCommandEntry("compile",            &CdlBuildableBody::parse_compile    ),
140
        CdlInterpreterCommandEntry("object",             &CdlBuildableBody::parse_object     ),
141
        CdlInterpreterCommandEntry("make_object",        &CdlBuildableBody::parse_make_object),
142
        CdlInterpreterCommandEntry("make",               &CdlBuildableBody::parse_make       ),
143
        CdlInterpreterCommandEntry("build_proc",         &CdlBuildableBody::parse_build_proc ),
144
        CdlInterpreterCommandEntry("",                   0                                   ),
145
    };
146
 
147
    for (int i = 0; commands[i].command != 0; i++) {
148
        std::vector<CdlInterpreterCommandEntry>::const_iterator j;
149
        for (j = parsers.begin(); j != parsers.end(); j++) {
150
            if (commands[i].name == j->name) {
151
                if (commands[i].command != j->command) {
152
                    CYG_FAIL("Property names are being re-used");
153
                }
154
                break;
155
            }
156
        }
157
        if (j == parsers.end()) {
158
            parsers.push_back(commands[i]);
159
        }
160
    }
161
    CdlNodeBody::add_property_parsers(parsers);
162
 
163
    CYG_REPORT_RETURN();
164
}
165
 
166
void
167
CdlBuildableBody::check_properties(CdlInterpreter interp)
168
{
169
    CYG_REPORT_FUNCNAME("CdlBuildable::check_properties");
170
    CYG_REPORT_FUNCARG2XV(this, interp);
171
    CYG_PRECONDITION_THISC();
172
    CYG_PRECONDITION_CLASSC(interp);
173
 
174
    // There are no real constraints on the number of compile
175
    // properties etc.
176
    // TODO: check that the relevant sources files exist,
177
    //       unless marked appropriately (build_proc can create
178
    //       new source files).
179
 
180
    CdlNodeBody::check_properties(interp);
181
 
182
    CYG_REPORT_RETURN();
183
}
184
 
185
//}}}
186
//{{{  Property parsers                 
187
 
188
// ----------------------------------------------------------------------------
189
// Syntax: compile <file1 file2 ...>
190
//
191
// There are a couple of checks that could be performed here:
192
//
193
// 1) does each listed file actually exist? Unfortunately that approach
194
//    falls foul of build_proc, which is allowed to generate source files
195
//    on the fly.
196
//
197
// 2) does the file have a recognised suffix such as .c or .cxx. This
198
//    relies libcdl having some way of knowing how to treat different
199
//    files.
200
//
201
// For now there are no validity checks.
202
//
203
// A future extension may allow dependencies to be listed, as an
204
// option. This would allow component vendors to specify that
205
// particular custom build steps should happen before particular
206
// compilations, a more robust approach than the current priority
207
// scheme.
208
 
209
int
210
CdlBuildableBody::parse_compile(CdlInterpreter interp, int argc, const char* argv[])
211
{
212
    CYG_REPORT_FUNCNAMETYPE("parse_compile", "result %d");
213
    static const char* options[] = {
214
        "library:",
215
 
216
    };
217
 
218
    int result = CdlParse::parse_stringvector_property(interp, argc, argv, CdlPropertyId_Compile, options, 0, true);
219
 
220
    CYG_REPORT_RETVAL(result);
221
    return result;
222
}
223
 
224
// ----------------------------------------------------------------------------
225
// A utility to break a custom build step down into its three components.
226
//
227
// A custom build step takes the form:
228
//     target : deps
229
//         rules
230
//
231
// This utility function takes a single string of this form and breaks
232
// it down into its constituent parts. 
233
//
234
// NOTE: this will need lots of extra code in future to allow for
235
// escaped characters, spaces in filenames, etc. For now just keep
236
// things simple.
237
 
238
bool
239
CdlBuildableBody::split_custom_build_step(std::string str_data, std::string& target, std::string& deps, std::string& rules,
240
                                          std::string& error_msg)
241
{
242
    CYG_REPORT_FUNCNAMETYPE("CdlBuildable::split_custom_build_step", "result %d");
243
 
244
    target      = "";
245
    deps        = "";
246
    rules       = "";
247
    error_msg   = "";
248
 
249
    const char* data  = str_data.c_str();
250
 
251
    // Skip any leading white space, and make sure that this leaves some real data.
252
    while (('\0' != *data) && isspace(*data)) {
253
        data++;
254
    }
255
    if ('\0' == *data) {
256
        error_msg = "no data in custom build_step";
257
        CYG_REPORT_RETVAL(false);
258
        return false;
259
    }
260
 
261
    // Now extract the target. This consists of any sequence of characters
262
    // upto space, tab, colon.
263
    for ( ; ('\0' != *data) && (':' != *data) && (' ' != *data) && ('\t' != *data); data++) {
264
        target += *data;
265
    }
266
    // Discard any spaces or tabs, they are of no interest
267
    while ((' ' == *data) || ('\t' == *data)) {
268
        data++;
269
    }
270
    // The current character should be a colon
271
    if (':' != *data) {
272
        error_msg = "expecting a colon `;' after the target `" + target + "'";
273
        CYG_REPORT_RETVAL(false);
274
        return false;
275
    }
276
 
277
    // Move past the colon, and skip any further spaces or tabs
278
    data++;
279
    while (('\0' != *data) && ((' ' == *data) || ('\t' == *data))) {
280
        data++;
281
    }
282
 
283
    // Everything from here until the end of line should be part of the deps field,
284
    // including white space. 
285
    while (('\0' != *data) && ('\n' != *data) && (';' != *data)) {
286
        deps += *data++;
287
    }
288
 
289
    if ("" == deps) {
290
        error_msg = "expecting dependency list after `" + target + ":'";
291
        CYG_REPORT_RETVAL(false);
292
        return false;
293
    }
294
 
295
    // Having some rules is compulsory.
296
    if ('\0' == *data) {
297
        error_msg = "expecting one or more rules after the dependency list";
298
        CYG_REPORT_RETVAL(false);
299
        return false;
300
    } else {
301
        // We are currently at \n or ;, move on to the actual rules
302
        data++;
303
    }
304
 
305
    // Rules consist of one or more lines. Any leading white space on a given
306
    // line should be discarded.
307
    while ('\0' != *data) {
308
        // Processing the current rule. Skip leading spaces and tabs
309
        while ((' ' == *data) || ('\t' == *data)) {
310
            data++;
311
        }
312
        // Now add everything up to the next newline or EOD to the rules.
313
        while (('\0' != *data) && ('\n' != *data)) {
314
            rules += *data++;
315
        }
316
 
317
        // Terminate this line of the rules with a newline, even if that
318
        // character is absent from the raw data.
319
        rules += '\n';
320
 
321
        // And ignore the newline in the raw data itself
322
        if ('\n' == *data) {
323
            data++;
324
        }
325
    }
326
 
327
    // Better make sure that there are some rules. All of the looping above
328
    // may just have left white space
329
    if ("" == rules) {
330
        error_msg = "no rules provided";
331
        CYG_REPORT_RETVAL(false);
332
        return false;
333
    }
334
 
335
    // Everything is ok.
336
 
337
    CYG_REPORT_RETVAL(true);
338
    return true;
339
}
340
 
341
 
342
// ----------------------------------------------------------------------------
343
// syntax: make <target> <rules>
344
//
345
// There is rather a lot of checking to be done.
346
//
347
// 1) the priority should be valid. In particular it should be a number
348
//    within a reasonable range.
349
//
350
// 2) the rules should take the form:
351
//    <target> : <deps> ;|\n rules
352
//
353
//    Where the target should be a single file, identical to the
354
//    first property argument.
355
 
356
static void
357
parse_make_final_check(CdlInterpreter interp, CdlProperty_String prop)
358
{
359
    CYG_REPORT_FUNCNAME("parse_make_final_check");
360
    CYG_PRECONDITION_CLASSC(interp);
361
    CYG_PRECONDITION_CLASSC(prop);
362
 
363
    std::string prio_string = prop->get_option("priority");
364
    if ("" != prio_string) {
365
        cdl_int tmp = 1;
366
        if (!Cdl::string_to_integer(prio_string, tmp)) {
367
            CdlParse::report_property_parse_error(interp, prop,
368
                                                  "Invalid priority option, priorities should be simple numbers.");
369
        } else {
370
            if ((tmp < 1) || (tmp > 1024)) {
371
                CdlParse::report_property_parse_error(interp, prop,
372
                                                      "Invalid priority value, priorities should be in the range 1 to 1024.");
373
            }
374
        }
375
    }
376
 
377
    std::string data    = prop->get_string();
378
    std::string target;
379
    std::string deps;
380
    std::string rules;
381
    std::string error_msg;
382
 
383
    if (!CdlBuildableBody::split_custom_build_step(data, target, deps, rules, error_msg)) {
384
        CdlParse::report_property_parse_error(interp, prop, "Invalid custom build step, " + error_msg);
385
    }
386
}
387
 
388
int
389
CdlBuildableBody::parse_make(CdlInterpreter interp, int argc, const char* argv[])
390
{
391
    CYG_REPORT_FUNCNAMETYPE("parse_make", "result %d");
392
    static const char* options[] = {
393
        "priority:",
394
 
395
    };
396
 
397
    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Make, options, &parse_make_final_check);
398
 
399
    CYG_REPORT_RETVAL(result);
400
    return result;
401
}
402
 
403
 
404
// ----------------------------------------------------------------------------
405
// syntax: make_object <target> <rules>
406
//
407
// The rules here are much the same as for the "make" property.
408
 
409
static void
410
parse_make_object_final_check(CdlInterpreter interp, CdlProperty_String prop)
411
{
412
    CYG_REPORT_FUNCNAME("parse_make_object_final_check");
413
    CYG_PRECONDITION_CLASSC(interp);
414
    CYG_PRECONDITION_CLASSC(prop);
415
 
416
 
417
    std::string prio_string = prop->get_option("priority");
418
    if ("" != prio_string) {
419
        cdl_int tmp = 1;
420
        if (!Cdl::string_to_integer(prio_string, tmp)) {
421
            CdlParse::report_property_parse_error(interp, prop,
422
                                                  "Invalid priority option, priorities should be simple numbers.");
423
        } else {
424
            if ((tmp < 1) || (tmp > 1024)) {
425
                CdlParse::report_property_parse_error(interp, prop,
426
                                                      "Invalid priority value, priorities should be in the range 1 to 1024.");
427
            }
428
        }
429
    }
430
 
431
    std::string data    = prop->get_string();
432
    std::string target;
433
    std::string deps;
434
    std::string rules;
435
    std::string error_msg;
436
 
437
    if (!CdlBuildableBody::split_custom_build_step(data, target, deps, rules, error_msg)) {
438
        CdlParse::report_property_parse_error(interp, prop, "Invalid custom build step, " + error_msg);
439
    }
440
}
441
 
442
int
443
CdlBuildableBody::parse_make_object(CdlInterpreter interp, int argc, const char* argv[])
444
{
445
    CYG_REPORT_FUNCNAMETYPE("parse_make_object", "result %d");
446
    static const char* options[] = {
447
        "library:",
448
        "priority:",
449
 
450
    };
451
 
452
    int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_MakeObject, options,
453
                                                 &parse_make_object_final_check);
454
 
455
    CYG_REPORT_RETVAL(result);
456
    return result;
457
}
458
 
459
 
460
// ----------------------------------------------------------------------------
461
// Syntax: object <file1> <file2> ...
462
 
463
int
464
CdlBuildableBody::parse_object(CdlInterpreter interp, int argc, const char* argv[])
465
{
466
    CYG_REPORT_FUNCNAMETYPE("parse_object", "result %d");
467
    static const char* options[] = {
468
        "library:",
469
 
470
    };
471
 
472
    int result = CdlParse::parse_stringvector_property(interp, argc, argv, CdlPropertyId_Object, options, 0, true);
473
 
474
    CYG_REPORT_RETVAL(result);
475
    return result;
476
}
477
 
478
// ----------------------------------------------------------------------------
479
// Syntax: build_proc { tcl code }
480
 
481
int
482
CdlBuildableBody::parse_build_proc(CdlInterpreter interp, int argc, const char* argv[])
483
{
484
    CYG_REPORT_FUNCNAMETYPE("parse_build_proc", "result %d");
485
 
486
    int result = CdlParse::parse_tclcode_property(interp, argc, argv, CdlPropertyId_BuildProc, 0, 0);
487
 
488
    CYG_REPORT_RETVAL(result);
489
    return result;
490
}
491
 
492
//}}}
493
//{{{  update_build_info()              
494
 
495
// ----------------------------------------------------------------------------
496
// Most of the work is done in update_all_build_info(). The update_build_info()
497
// merely checks the active and enabled state first.
498
void
499
CdlBuildableBody::update_build_info(CdlBuildInfo_Loadable& build_info, std::string library) const
500
{
501
    CYG_REPORT_FUNCNAME("CdlBuildable::update_build_info");
502
    CYG_REPORT_FUNCARG2XV(this, &build_info);
503
    CYG_PRECONDITION_THISC();
504
    CYG_PRECONDITIONC("" != library);
505
 
506
    if (!is_active()) {
507
        CYG_REPORT_RETURN();
508
        return;
509
    }
510
 
511
    CdlConstValuable valuable = dynamic_cast<CdlConstValuable>(this);
512
    if (0 != valuable) {
513
        if (!valuable->is_enabled()) {
514
            CYG_REPORT_RETURN();
515
            return;
516
        }
517
    }
518
 
519
    update_all_build_info(build_info, library);
520
 
521
    CYG_REPORT_RETURN();
522
}
523
 
524
//}}}
525
//{{{  update_all_build_info()          
526
 
527
// ----------------------------------------------------------------------------
528
// There are four properties to be considered, each of which may occur
529
// multiple times: "compile", "object", "make_object", and "make".
530
// Each of these will result in separate additions to the build_info
531
// structure.
532
 
533
void
534
CdlBuildableBody::update_all_build_info(CdlBuildInfo_Loadable& build_info, std::string package_library) const
535
{
536
    CYG_REPORT_FUNCNAME("CdlBuildable::update_all_build_info");
537
    CYG_REPORT_FUNCARG2XV(this, &build_info);
538
    CYG_PRECONDITION_THISC();
539
    CYG_PRECONDITIONC("" != package_library);
540
 
541
    // Get some information about the owning loadable first.
542
    CdlLoadable loadable        = get_owner();
543
    CYG_ASSERT_CLASSC(loadable);
544
    std::string directory       = loadable->get_directory();
545
    std::string repository      = loadable->get_repository();
546
    CYG_ASSERTC("" != directory);
547
    CYG_ASSERTC("" != repository);
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", repository);
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
const 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
const 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_repository() + "/" + 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.repository    = get_repository();
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 const char* options[] = {
1426
        "file:",
1427
        "format:",
1428
 
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
    const char* options[] = {
1509
        "file:",
1510
 
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
#if defined(_WIN32) || defined(__CYGWIN__)
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
#if defined(_WIN32) || defined(__CYGWIN__)
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
//}}}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.