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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [tools/] [src/] [libcdl/] [build.cxx] - Blame information for rev 174

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

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