feisty meow concerns codebase 2.140
write_build_config.cpp
Go to the documentation of this file.
1/*****************************************************************************\
2* *
3* Name : write_build_config *
4* Author : Chris Koeritz *
5* *
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\*****************************************************************************/
14
15#include "write_build_config.h"
16
19#include <basis/functions.h>
22#include <filesystem/filename.h>
25#include <structures/set.h>
29
30#include <stdio.h>
31
32using namespace application;
33using namespace basis;
34using namespace configuration;
35using namespace filesystem;
36using namespace loggers;
37using namespace structures;
38using namespace textual;
39using namespace versions;
40
41const int MAX_LINE_SIZE = 2048;
43
44const int MAX_HEADER_FILE = 128 * KILOBYTE;
46
47const char *DEFINITIONS_STATEMENT = "DEFINITIONS";
49
50const char *EXPORT_STATEMENT = "export ";
52
53// make conditionals that we will eat.
54const char *IFEQ_STATEMENT = "ifeq";
55const char *IFNEQ_STATEMENT = "ifneq";
56const char *ENDIF_STATEMENT = "endif";
57
58#undef LOG
59#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
60
63 _end_matter(new astring),
64 _ver(new version),
65 _nesting(0)
66{}
67
69{
70 WHACK(_end_matter);
71 WHACK(_ver);
72}
73
75{
76 static string_set _hidden;
77 static bool _initted = false;
78 if (!_initted) {
79 _hidden += "DEBUG";
80 _hidden += "OPTIMIZE";
81 _hidden += "STRICT_WARNINGS";
82 }
83 return _hidden;
84}
85
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; \
90}
91
93 const astring &value, astring &accumulator)
94{
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;
103 accumulator += "\n";
104 accumulator += " #define ";
105 accumulator += symbol;
106 accumulator += " \"";
107 accumulator += value;
108 accumulator += "\"\n";
109 accumulator += " #endif\n";
110 return common::OKAY;
111}
112
114 const astring &value)
115{
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; }
124 return false;
125}
126
128{
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)) {
131 _nesting++;
132 return true;
133 }
134 if (to_check.compare(ENDIF_STATEMENT, 0, 0, int(strlen(ENDIF_STATEMENT)), true)) {
135 _nesting--;
136 return true;
137 }
138 return false;
139}
140
142 const astring &value, astring &cfg_accumulator, astring &ver_accumulator)
143{
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);
154 if (_nesting)
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;
160 }
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);
164}
165
167 (const astring &embedded_value, astring &accumulator)
168{
169 FUNCDEF("output_definition_macro");
170//LOG(astring("into output def with: ") + embedded_value);
172 t.parse(embedded_value);
173 if (!t.symbols())
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]);
177 if (_nesting)
178 ADD_COMMENT_RETURN(t.table().name(0), t.table()[0]);
179 return output_macro(t.table().name(0), t.table()[0], accumulator);
180}
181
183 const astring &new_contents)
184{
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);
192if (read < 1) LOG("why is existing header contentless?");
193 if (read > 0) {
194 astring found(astring::UNTERMINATED, (char *)file_contents.observe(),
195 read);
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;
200 }
201 }
202 }
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.
206 if (write_header) {
207 // we actually want to blast out a new file.
208 byte_filer build_header(filename, "wb");
209 if (!build_header.good()) {
210 continuable_error(static_class_name(), func, astring("could not create "
211 "build header file in ") + build_header.name());
212 return false;
213 } else {
214 build_header.write(new_contents);
215 LOG(astring(static_class_name()) + ": wrote config to "
216 + build_header.name());
217 }
218 } else {
219 // nothing has changed.
220// LOG(astring(static_class_name()) + ": config already up to date in "
221// + filename);
222 }
223 return true;
224}
225
227{
228 FUNCDEF("execute");
229 SETUP_CONSOLE_LOGGER; // override the file_logger from app_shell.
230
231 // find our build ini file.
232 astring repodir = environment::get("FEISTY_MEOW_APEX");
233
234 // the below code should never be needed for a properly configured build.
235#ifdef __WIN32__
236 if (!repodir) repodir = "l:";
237#else // unix and other locations.
238 if (!repodir)
239 repodir = environment::get("HOME") + "/hoople";
240#endif
241
242 astring fname;
243 // where we seek out our build settings.
244 astring parmfile = environment::get("BUILD_PARAMETER_FILE");
245 if (parmfile.t()) fname = parmfile;
246 else fname = repodir + "/build.ini";
247
248 // find our storage area for the build headers. we know a couple build
249 // configurations by now, but this should really be coming out of a config
250 // file instead.
251 astring versions_directory = environment::get("FEISTY_MEOW_GENERATED_STORE");
252 // we keep our version files one level below the top of the generated store.
253 versions_directory += "/versions";
254 if (!filename(versions_directory).good()) {
256 astring("failed to locate the library folder storing the generated files."));
257 }
258
259 // these are very specific paths, but they really are where we expect to
260 // see the headers.
261
262 astring cfg_header_filename = versions_directory + "/__build_configuration.h";
263 astring ver_header_filename = versions_directory + "/__build_version.h";
264
265 // open the ini file for reading.
266 byte_filer ini(fname, "r");
267 if (!ini.good())
268 non_continuable_error(static_class_name(), func, astring("failed to open "
269 "build configuration file for reading at ") + ini.name());
270//hmmm: parameterize the build ini thing above!
271
272 // now we build strings that represents the output files we want to create.
273 astring cfg_accumulator;
274 astring ver_accumulator;
275
276 // odd indentation below comes from the strings being c++ code that will be
277 // written to a file.
278//hmmm: another location to fix!!!
279 cfg_accumulator += "\
280#ifndef BUILD_SYSTEM_CONFIGURATION\n\
281#define BUILD_SYSTEM_CONFIGURATION\n\n\
282 // This file provides all of the code flags which were used when this build\n\
283 // was generated. Some of the items in the build configuration have been\n\
284 // stripped out because they are not used.\n\n";
285 ver_accumulator += "\
286#ifndef BUILD_VERSION_CONFIGURATION\n\
287#define BUILD_VERSION_CONFIGURATION\n\n\
288 // This file provides the version macros for this particular build.\n\n";
289
290 // iterate through the entries we read in earlier and generate our header.
291 astring symbol, value;
292 astring buffer;
293 while (!ini.eof()) {
294 int chars = ini.getline(buffer, MAX_LINE_SIZE);
295 if (!chars) continue; // hmmm: what does no chars mean?
296
298 t.parse(buffer);
299 if (!t.symbols()) continue; // not so good.
300
301 // pull out the first pair we found and try to parse it.
302 symbol = t.table().name(0);
303 value = t.table()[0];
305
306 // clean out + characters that can come from += declarations.
307 while ( (symbol[symbol.end()] == '+') || (symbol[symbol.end()] == ':') ) {
308 symbol.zap(symbol.end(), symbol.end());
310 }
311
312 if (symbol[0] == '#') continue; // toss out any comments.
313
314 if (!symbol) continue; // seems like that one didn't work out so well.
315
316 if (symbol.compare(EXPORT_STATEMENT, 0, 0, int(strlen(EXPORT_STATEMENT)), true)) {
317 // clean out export statements in front of our variables.
318 symbol.zap(0, int(strlen(EXPORT_STATEMENT) - 1));
319 }
320
321 // check for a make-style macro definition.
322 if (symbol.compare(DEFINITIONS_STATEMENT, 0, 0, int(strlen(DEFINITIONS_STATEMENT)), true)) {
323 // found a macro definition. write that up after pulling the real
324 // contents out.
325 output_definition_macro(value, cfg_accumulator);
326 } else {
327 // this one is hopefully a very tasty specialized macro. we will
328 // show it with added text to make it unique.
329 output_decorated_macro(symbol, value, cfg_accumulator, ver_accumulator);
330 }
331 }
332
333 // write some calculated macros now.
334 ver_accumulator += "\n";
335 ver_accumulator += " // calculated macros are dropped in here.\n\n";
336
337 // we write our version in a couple forms. hopefully we accumulated it.
338
339 // this one is the same as the file version currently (below), but may go to
340 // a different version at some point.
341 ver_accumulator += " #define __build_SYSTEM_VERSION \"";
342 ver_accumulator += _ver->flex_text_form();
343 ver_accumulator += "\"\n\n";
344
345 // we drop in the version as numbers also, since the version RC wants that.
346 ver_accumulator += " #define __build_FILE_VERSION_COMMAS ";
347 ver_accumulator += _ver->flex_text_form(version::COMMAS);
348 ver_accumulator += "\n";
349 // another form of the file version for dotted notation.
350 ver_accumulator += " #define __build_FILE_VERSION \"";
351 ver_accumulator += _ver->flex_text_form();
352 ver_accumulator += "\"\n";
353
354 // product version is just the first two parts.
356 _ver->set_component(version::BUILD, "0");
357 // product version as a list of numbers.
358 ver_accumulator += " #define __build_PRODUCT_VERSION_COMMAS ";
359 ver_accumulator += _ver->flex_text_form(version::COMMAS);
360 ver_accumulator += "\n";
361 // another form of the product version for use as a string.
362 ver_accumulator += " #define __build_PRODUCT_VERSION \"";
363 ver_accumulator += _ver->flex_text_form(version::DOTS, version::MINOR);
364 ver_accumulator += "\"\n";
365
366 // write a blob of comments at the end with what we didn't use.
367 cfg_accumulator += "\n";
368 cfg_accumulator += "/*\n";
369 cfg_accumulator += "These settings were not used:\n";
370 cfg_accumulator += *_end_matter;
371 cfg_accumulator += "*/\n";
372 cfg_accumulator += "\n";
373 cfg_accumulator += "#endif /* outer guard */\n\n";
374
375 // finish up the version file also.
376 ver_accumulator += "\n";
377 ver_accumulator += "#endif /* outer guard */\n\n";
378
379 if (!write_output_file(cfg_header_filename, cfg_accumulator)) {
380 LOG(astring("failed writing output file ") + cfg_header_filename);
381 }
382 if (!write_output_file(ver_header_filename, ver_accumulator)) {
383 LOG(astring("failed writing output file ") + ver_header_filename);
384 }
385
386 return 0;
387}
388
390
391#ifdef __BUILD_STATIC_APPLICATION__
392 // static dependencies found by buildor_gen_deps.sh:
393 #include <algorithms/sorts.cpp>
398 #include <basis/astring.cpp>
400 #include <basis/environment.cpp>
401 #include <basis/guards.cpp>
402 #include <basis/mutex.cpp>
403 #include <basis/utf_conversion.cpp>
411 #include <filesystem/directory.cpp>
412 #include <filesystem/filename.cpp>
413 #include <loggers/combo_logger.cpp>
416 #include <loggers/file_logger.cpp>
419 #include <structures/checksums.cpp>
426 #include <textual/parser_bits.cpp>
428 #include <timely/earth_time.cpp>
429 #include <timely/time_stamp.cpp>
430 #include <versions/version_ini.cpp>
431#endif // __BUILD_STATIC_APPLICATION__
432
#define read
Definition Xos2defs.h:38
#define LOG(s)
const int MAX_LINE_SIZE
The application_shell is a base object for console programs.
const contents * observe() const
Returns a pointer to the underlying C array of data.
Definition array.h:172
Provides a dynamically resizable ASCII character string.
Definition astring.h:35
bool t() const
t() is a shortcut for the string being "true", as in non-empty.
Definition astring.h:97
virtual void zap(int start, int end)
Deletes the characters between "start" and "end" inclusively.
Definition astring.cpp:524
void strip_spaces(how_to_strip way=FROM_BOTH_SIDES)
removes excess space characters from string's beginning, end or both.
Definition astring.h:325
bool equal_to(const char *that) const
returns true if "that" is equal to this.
Definition astring.cpp:159
int end() const
returns the index of the last (non-null) character in the string.
Definition astring.h:86
bool compare(const astring &to_compare, int start_first, int start_second, int count, bool case_sensitive) const
Compares "this" string with "to_compare".
Definition astring.cpp:813
bool contains(const astring &to_find) const
Returns true if "to_find" is contained in this string or false if not.
Definition astring.cpp:162
A very common template for a dynamic array of bytes.
Definition byte_array.h:36
static astring get(const astring &variable_name)
looks up the "variable_name" in the current environment variables.
Outcomes describe the state of completion for an operation.
Definition outcome.h:31
Manages a bank of textual definitions of variables.
const structures::string_table & table() const
provides a constant peek at the string_table holding the values.
int symbols() const
returns the number of entries in the variable_tokenizer.
bool parse(const basis::astring &to_tokenize)
parses the string using our established sentinel characters.
Provides file managment services using the standard I/O support.
Definition byte_filer.h:32
int getline(basis::abyte *buffer, int desired_size)
reads a line of text (terminated by a return) into the "buffer".
int write(const basis::abyte *buffer, int buffer_size)
writes "buffer_size" bytes into the file from "buffer".
const basis::astring & name() const
returns the file name that the object is operating on.
int read(basis::abyte *buffer, int buffer_size)
reads "buffer_size" bytes from the file into "buffer".
bool eof()
returns true if the cursor is at (or after) the end of the file.
bool good()
returns true if the file seems to be in the appropriate desired state.
Provides operations commonly needed on file names.
Definition filename.h:64
A simple object that wraps a templated set of strings.
Definition set.h:168
const basis::astring & name(int index) const
returns the name held at the "index".
Holds a file's version identifier.
void set_component(int index, const basis::astring &to_set)
sets the component at "index" to "to_set".
basis::astring flex_text_form(version_style style=DOTS, int including=-1) const
returns a textual form of the version number.
const structures::string_set & exclusions()
returns the set of symbols that we will not include in the header.
bool write_output_file(const basis::astring &filename, const basis::astring &contents)
writes "contents" to "filename" if it differs from current contents.
basis::outcome output_macro(const basis::astring &symbol, const basis::astring &value, basis::astring &accumulator)
sends a macro definition for "symbol" with "value" to "accumulator".
basis::outcome output_definition_macro(const basis::astring &embedded_value, basis::astring &accumulator)
parses a 'name=value' pair out of "embedded_value" and writes a macro.
bool check_nesting(const basis::astring &to_check)
if "to_check" is a make conditional, the nesting level is adjusted.
basis::outcome output_decorated_macro(const basis::astring &symbol, const basis::astring &value, basis::astring &cfg_accumulator, basis::astring &ver_accumulator)
produces a new macro by adding a uniquifying string to "symbol".
int execute()
performs the main action of creating our build configuration header.
bool process_version_parts(const basis::astring &symbol, const basis::astring &value)
checks on "symbol" to see if it's a version component. stores if so.
#define SETUP_CONSOLE_LOGGER
< a macro that retasks the program-wide logger as a console_logger.
#define continuable_error(c, f, i)
#define non_continuable_error(c, f, i)
an extra piece of information used, if available, in bounds_halt below.
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition enhance_cpp.h:54
Provides macros that implement the 'main' program of an application.
#define HOOPLE_MAIN(obj_name, obj_args)
options that should work for most unix and linux apps.
Definition hoople_main.h:61
Implements an application lock to ensure only one is running at once.
The guards collection helps in testing preconditions and reporting errors.
Definition array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition functions.h:121
const int KILOBYTE
Number of bytes in a kilobyte.
A platform independent way to obtain the timestamp of a file.
A logger that sends to the console screen using the standard output device.
A dynamic container class that holds any kind of object via pointers.
Definition amorph.h:55
#define static_class_name()
Aids in achievement of platform independence.
const char * EXPORT_STATEMENT
a tag we see on variables to be inherited by subshells
const int MAX_HEADER_FILE
an excessively long allowance for the maximum generated header size.
const char * DEFINITIONS_STATEMENT
the tag we see in the config file for directly compatible macros.
const char * IFNEQ_STATEMENT
const char * ENDIF_STATEMENT
const int MAX_LINE_SIZE
we should never see an ini line longer than this.
#define ADD_COMMENT_RETURN(sym, val)
const char * IFEQ_STATEMENT