1 /*****************************************************************************\
3 * Name : write_build_config *
4 * Author : Chris Koeritz *
6 *******************************************************************************
7 * Copyright (c) 1995-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
15 #include "write_build_config.h"
17 #include <application/hoople_main.h>
18 #include <application/windoze_helper.h>
19 #include <basis/functions.h>
20 #include <configuration/variable_tokenizer.h>
21 #include <filesystem/byte_filer.h>
22 #include <filesystem/filename.h>
23 #include <loggers/console_logger.h>
24 #include <loggers/critical_events.h>
25 #include <structures/set.h>
26 #include <structures/static_memory_gremlin.h>
27 #include <structures/string_table.h>
28 #include <versions/version_ini.h>
32 using namespace application;
33 using namespace basis;
34 using namespace configuration;
35 using namespace filesystem;
36 using namespace loggers;
37 using namespace structures;
38 using namespace textual;
39 using namespace versions;
41 const int MAX_LINE_SIZE = 2048;
42 //!< we should never see an ini line longer than this.
44 const int MAX_HEADER_FILE = 128 * KILOBYTE;
45 //!< an excessively long allowance for the maximum generated header size.
47 const char *DEFINITIONS_STATEMENT = "DEFINITIONS";
48 //!< the tag we see in the config file for directly compatible macros.
50 const char *EXPORT_STATEMENT = "export ";
51 //!< a tag we see on variables to be inherited by subshells
53 // make conditionals that we will eat.
54 const char *IFEQ_STATEMENT = "ifeq";
55 const char *IFNEQ_STATEMENT = "ifneq";
56 const char *ENDIF_STATEMENT = "endif";
59 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
61 write_build_config::write_build_config()
62 : application_shell(),
63 _end_matter(new astring),
68 write_build_config::~write_build_config()
74 const string_set &write_build_config::exclusions()
76 static string_set _hidden;
77 static bool _initted = false;
80 _hidden += "OPTIMIZE";
81 _hidden += "STRICT_WARNINGS";
86 // adds some more material to dump at the end of the file.
87 #define ADD_COMMENT_RETURN(sym, val) { \
88 *_end_matter += astring(" ") + sym + " = " + val + "\n"; \
89 return common::OKAY; \
92 outcome write_build_config::output_macro(const astring &symbol,
93 const astring &value, astring &accumulator)
95 // drop any excluded items to avoid interfering with devstu solution.
96 if (exclusions().member(symbol))
97 ADD_COMMENT_RETURN(symbol, value);
98 // drop any malformed symbols or values.
99 if (symbol.contains("\"") || value.contains("\""))
100 ADD_COMMENT_RETURN(symbol, value);
101 accumulator += " #ifndef ";
102 accumulator += symbol;
104 accumulator += " #define ";
105 accumulator += symbol;
106 accumulator += " \"";
107 accumulator += value;
108 accumulator += "\"\n";
109 accumulator += " #endif\n";
113 bool write_build_config::process_version_parts(const astring &symbol,
114 const astring &value)
116 if (symbol.equal_to("major"))
117 { _ver->set_component(version::MAJOR, value); return true; }
118 if (symbol.equal_to("minor"))
119 { _ver->set_component(version::MINOR, value); return true; }
120 if (symbol.equal_to("revision"))
121 { _ver->set_component(version::REVISION, value); return true; }
122 if (symbol.equal_to("build"))
123 { _ver->set_component(version::BUILD, value); return true; }
127 bool write_build_config::check_nesting(const astring &to_check)
129 if (to_check.compare(IFEQ_STATEMENT, 0, 0, int(strlen(IFEQ_STATEMENT)), true)
130 || to_check.compare(IFNEQ_STATEMENT, 0, 0, int(strlen(IFNEQ_STATEMENT)), true)) {
134 if (to_check.compare(ENDIF_STATEMENT, 0, 0, int(strlen(ENDIF_STATEMENT)), true)) {
141 outcome write_build_config::output_decorated_macro(const astring &symbol_in,
142 const astring &value, astring &cfg_accumulator, astring &ver_accumulator)
144 // make sure we catch any conditionals.
145 if (check_nesting(symbol_in))
146 ADD_COMMENT_RETURN(symbol_in, value);
147 // toss out any exclusions.
148 if (exclusions().member(symbol_in))
149 ADD_COMMENT_RETURN(symbol_in, value);
150 if (symbol_in.contains("\"") || value.contains("\""))
151 ADD_COMMENT_RETURN(symbol_in, value);
152 if (symbol_in[0] == '[')
153 ADD_COMMENT_RETURN(symbol_in, value);
155 ADD_COMMENT_RETURN(symbol_in, value);
156 // switch the output stream based on whether its a version component or not.
157 astring *the_accumulator = &cfg_accumulator;
158 if (process_version_parts(symbol_in, value)) {
159 the_accumulator = &ver_accumulator;
161 // add a special tag so that we won't be colliding with other names.
162 astring symbol = astring("__build_") + symbol_in;
163 return output_macro(symbol, value, *the_accumulator);
166 outcome write_build_config::output_definition_macro
167 (const astring &embedded_value, astring &accumulator)
169 // FUNCDEF("output_definition_macro");
170 //LOG(astring("into output def with: ") + embedded_value);
171 variable_tokenizer t;
172 t.parse(embedded_value);
174 ADD_COMMENT_RETURN("bad definition", embedded_value);
175 if (exclusions().member(t.table().name(0)))
176 ADD_COMMENT_RETURN(t.table().name(0), t.table()[0]);
178 ADD_COMMENT_RETURN(t.table().name(0), t.table()[0]);
179 return output_macro(t.table().name(0), t.table()[0], accumulator);
182 bool write_build_config::write_output_file(const astring &filename,
183 const astring &new_contents)
185 FUNCDEF("write_output_file");
186 // now read the soon-to-be output file so we can check its current state.
187 bool write_header = true;
188 byte_filer check_header(filename, "rb");
189 if (check_header.good()) {
190 byte_array file_contents;
191 int read = check_header.read(file_contents, MAX_HEADER_FILE);
192 if (read < 1) LOG("why is existing header contentless?");
194 astring found(astring::UNTERMINATED, (char *)file_contents.observe(),
196 //LOG(astring("got existing content:\n-----\n") + found + "\n-----\n");
197 //LOG(astring("new_content has:\n-----\n") + new_contents + "\n-----\n");
198 if (found == new_contents) {
199 write_header = false;
203 // writing only occurs when we know that the build configurations have
204 // changed. if the file is the same, we definitely don't want to write
205 // it because pretty much all the other files depend on it.
207 // we actually want to blast out a new file.
208 byte_filer build_header(filename, "wb");
209 if (!build_header.good())
210 non_continuable_error(static_class_name(), func, astring("failed to create "
211 "build header file in ") + build_header.filename());
212 build_header.write(new_contents);
213 LOG(astring(static_class_name()) + ": wrote config to "
214 + build_header.filename());
216 // nothing has changed.
217 // LOG(astring(static_class_name()) + ": config already up to date in "
223 int write_build_config::execute()
226 SETUP_CONSOLE_LOGGER; // override the file_logger from app_shell.
228 // find our build ini file.
229 astring repodir = environment::get("FEISTY_MEOW_DIR");
231 // the below code should never be needed for a properly configured build.
233 if (!repodir) repodir = "l:";
234 #else // unix and other locations.
236 repodir = environment::get("HOME") + "/hoople";
240 // where we seek out our build settings.
241 astring parmfile = environment::get("BUILD_PARAMETER_FILE");
242 if (parmfile.t()) fname = parmfile;
243 else fname = repodir + "/build.ini";
245 // find our storage area for the build headers. we know a couple build
246 // configurations by now, but this should really be coming out of a config
248 astring library_directory = repodir + "/nucleus/library";
249 if (!filename(library_directory).good()) {
250 non_continuable_error(static_class_name(), func,
251 astring("failed to locate the library folder storing the generated files."));
254 // these are very specific paths, but they really are where we expect to
257 astring cfg_header_filename = library_directory + "/"
258 "__build_configuration.h";
259 astring ver_header_filename = library_directory + "/"
262 // open the ini file for reading.
263 byte_filer ini(fname, "r");
265 non_continuable_error(static_class_name(), func, astring("failed to open "
266 "build configuration file for reading at ") + ini.filename());
267 //hmmm: parameterize the build ini thing above!
269 // now we build strings that represents the output files we want to create.
270 astring cfg_accumulator;
271 astring ver_accumulator;
273 // odd indentation below comes from the strings being c++ code that will be
274 // written to a file.
275 //hmmm: another location to fix!!!
276 cfg_accumulator += "\
277 #ifndef BUILD_SYSTEM_CONFIGURATION\n\
278 #define BUILD_SYSTEM_CONFIGURATION\n\n\
279 // This file provides all of the code flags which were used when this build\n\
280 // was generated. Some of the items in the build configuration have been\n\
281 // stripped out because they are not used.\n\n";
282 ver_accumulator += "\
283 #ifndef BUILD_VERSION_CONFIGURATION\n\
284 #define BUILD_VERSION_CONFIGURATION\n\n\
285 // This file provides the version macros for this particular build.\n\n";
287 // iterate through the entries we read in earlier and generate our header.
288 astring symbol, value;
291 int chars = ini.getline(buffer, MAX_LINE_SIZE);
292 if (!chars) continue; // hmmm.
294 variable_tokenizer t;
296 if (!t.symbols()) continue; // not so good.
298 // pull out the first pair we found and try to parse it.
299 symbol = t.table().name(0);
300 value = t.table()[0];
301 symbol.strip_spaces(astring::FROM_BOTH_SIDES);
303 // clean out + characters that can come from += declarations.
304 while ( (symbol[symbol.end()] == '+') || (symbol[symbol.end()] == ':') ) {
305 symbol.zap(symbol.end(), symbol.end());
306 symbol.strip_spaces(astring::FROM_END);
309 if (symbol[0] == '#') continue; // toss out any comments.
311 if (!symbol) continue; // seems like that one didn't work out so well.
313 if (symbol.compare(EXPORT_STATEMENT, 0, 0, int(strlen(EXPORT_STATEMENT)), true)) {
314 // clean out export statements in front of our variables.
315 symbol.zap(0, int(strlen(EXPORT_STATEMENT) - 1));
318 // check for a make-style macro definition.
319 if (symbol.compare(DEFINITIONS_STATEMENT, 0, 0, int(strlen(DEFINITIONS_STATEMENT)), true)) {
320 // found a macro definition. write that up after pulling the real
322 output_definition_macro(value, cfg_accumulator);
324 // this one is hopefully a very tasty specialized macro. we will
325 // show it with added text to make it unique.
326 output_decorated_macro(symbol, value, cfg_accumulator, ver_accumulator);
330 // write some calculated macros now.
331 ver_accumulator += "\n";
332 ver_accumulator += " // calculated macros are dropped in here.\n\n";
334 // we write our version in a couple forms. hopefully we accumulated it.
336 // this one is the same as the file version currently (below), but may go to
337 // a different version at some point.
338 ver_accumulator += " #define __build_SYSTEM_VERSION \"";
339 ver_accumulator += _ver->flex_text_form();
340 ver_accumulator += "\"\n\n";
342 // we drop in the version as numbers also, since the version RC wants that.
343 ver_accumulator += " #define __build_FILE_VERSION_COMMAS ";
344 ver_accumulator += _ver->flex_text_form(version::COMMAS);
345 ver_accumulator += "\n";
346 // another form of the file version for dotted notation.
347 ver_accumulator += " #define __build_FILE_VERSION \"";
348 ver_accumulator += _ver->flex_text_form();
349 ver_accumulator += "\"\n";
351 // product version is just the first two parts.
352 _ver->set_component(version::REVISION, "0");
353 _ver->set_component(version::BUILD, "0");
354 // product version as a list of numbers.
355 ver_accumulator += " #define __build_PRODUCT_VERSION_COMMAS ";
356 ver_accumulator += _ver->flex_text_form(version::COMMAS);
357 ver_accumulator += "\n";
358 // another form of the product version for use as a string.
359 ver_accumulator += " #define __build_PRODUCT_VERSION \"";
360 ver_accumulator += _ver->flex_text_form(version::DOTS, version::MINOR);
361 ver_accumulator += "\"\n";
363 // write a blob of comments at the end with what we didn't use.
364 cfg_accumulator += "\n";
365 cfg_accumulator += "/*\n";
366 cfg_accumulator += "These settings were not used:\n";
367 cfg_accumulator += *_end_matter;
368 cfg_accumulator += "*/\n";
369 cfg_accumulator += "\n";
370 cfg_accumulator += "#endif /* outer guard */\n\n";
372 // finish up the version file also.
373 ver_accumulator += "\n";
374 ver_accumulator += "#endif /* outer guard */\n\n";
376 if (!write_output_file(cfg_header_filename, cfg_accumulator)) {
377 LOG(astring("failed writing output file ") + cfg_header_filename);
379 if (!write_output_file(ver_header_filename, ver_accumulator)) {
380 LOG(astring("failed writing output file ") + ver_header_filename);
386 HOOPLE_MAIN(write_build_config, )
388 #ifdef __BUILD_STATIC_APPLICATION__
389 // static dependencies found by buildor_gen_deps.sh:
390 #include <application/application_shell.cpp>
391 #include <application/command_line.cpp>
392 #include <basis/astring.cpp>
393 #include <basis/common_outcomes.cpp>
394 #include <basis/environment.cpp>
395 #include <basis/mutex.cpp>
396 #include <basis/utf_conversion.cpp>
397 #include <configuration/application_configuration.cpp>
398 #include <configuration/configurator.cpp>
399 #include <configuration/ini_configurator.cpp>
400 #include <configuration/ini_parser.cpp>
401 #include <configuration/table_configurator.cpp>
402 #include <configuration/variable_tokenizer.cpp>
403 #include <filesystem/byte_filer.cpp>
404 #include <filesystem/directory.cpp>
405 #include <filesystem/filename.cpp>
406 #include <loggers/combo_logger.cpp>
407 #include <loggers/console_logger.cpp>
408 #include <loggers/critical_events.cpp>
409 #include <loggers/file_logger.cpp>
410 #include <loggers/program_wide_logger.cpp>
411 #include <structures/bit_vector.cpp>
412 #include <structures/checksums.cpp>
413 #include <structures/object_packers.cpp>
414 #include <structures/static_memory_gremlin.cpp>
415 #include <structures/string_hasher.cpp>
416 #include <structures/string_table.cpp>
417 #include <structures/version_record.cpp>
418 #include <textual/byte_formatter.cpp>
419 #include <textual/parser_bits.cpp>
420 #include <textual/string_manipulation.cpp>
421 #include <timely/earth_time.cpp>
422 #include <timely/time_stamp.cpp>
423 #include <versions/version_ini.cpp>
424 #endif // __BUILD_STATIC_APPLICATION__