write_build_config.cpp

Go to the documentation of this file.
00001 /*****************************************************************************\
00002 *                                                                             *
00003 *  Name   : write_build_config                                                *
00004 *  Author : Chris Koeritz                                                     *
00005 *                                                                             *
00006 *******************************************************************************
00007 * Copyright (c) 1995-$now By Author.  This program is free software; you can  *
00008 * redistribute it and/or modify it under the terms of the GNU General Public  *
00009 * License as published by the Free Software Foundation; either version 2 of   *
00010 * the License or (at your option) any later version.  This is online at:      *
00011 *     http://www.fsf.org/copyleft/gpl.html                                    *
00012 * Please send any updates to: fred@gruntose.com                               *
00013 \*****************************************************************************/
00014 
00015 #include "write_build_config.h"
00016 
00017 #include <basis/function.h>
00018 #include <basis/portable.h>
00019 #include <basis/set.cpp>
00020 #include <basis/version_record.h>
00021 #include <data_struct/string_table.h>
00022 #include <opsystem/byte_filer.h>
00023 #include <loggers/console_logger.h>
00024 #include <data_struct/static_memory_gremlin.h>
00025 #include <opsystem/version_ini.h>
00026 #include <textual/tokenizer.h>
00027 
00028 #include <stdio.h>
00029 
00030 const int MAX_LINE_SIZE = 2048;
00032 
00033 const int MAX_HEADER_FILE = 128 * KILOBYTE;
00035 
00036 const char *DEFINITIONS_STATEMENT = "DEFINITIONS";
00038 
00039 const char *EXPORT_STATEMENT = "export ";
00041 
00042 // make conditionals that we will eat.
00043 const char *IFEQ_STATEMENT = "ifeq";
00044 const char *IFNEQ_STATEMENT = "ifneq";
00045 const char *ENDIF_STATEMENT = "endif";
00046 
00047 #undef LOG
00048 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger(), to_print)
00049 
00050 write_build_config::write_build_config()
00051 : application_shell(static_class_name()),
00052   _end_matter(new istring),
00053   _ver(new version),
00054   _nesting(0)
00055 {}
00056 
00057 write_build_config::~write_build_config()
00058 {
00059   WHACK(_end_matter);
00060   WHACK(_ver);
00061 }
00062 
00063 const string_set &write_build_config::exclusions() 
00064 {
00065   static string_set _hidden;
00066   static bool _initted = false;
00067   if (!_initted) {
00068     _hidden += "DEBUG";
00069     _hidden += "OPTIMIZE";
00070     _hidden += "STRICT_WARNINGS";
00071     _hidden += "NO_SETUP";
00072   }
00073   return _hidden;
00074 }
00075 
00076 // adds some more material to dump at the end of the file.
00077 #define ADD_COMMENT_RETURN(sym, val) { \
00078   *_end_matter += istring("  ") + sym + " = " + val + "\n"; \
00079   return common::OKAY; \
00080 }
00081 
00082 outcome write_build_config::output_macro(const istring &symbol,
00083     const istring &value, istring &accumulator)
00084 {
00085   // drop any excluded items to avoid interfering with devstu solution.
00086   if (exclusions().member(symbol))
00087     ADD_COMMENT_RETURN(symbol, value);
00088   // drop any malformed symbols or values.
00089   if (symbol.contains("\"") || value.contains("\""))
00090     ADD_COMMENT_RETURN(symbol, value);
00091   accumulator += "  #ifndef ";
00092   accumulator += symbol;
00093   accumulator += "\n";
00094   accumulator += "    #define ";
00095   accumulator += symbol;
00096   accumulator += " \"";
00097   accumulator += value;
00098   accumulator += "\"\n";
00099   accumulator += "  #endif\n";
00100   return common::OKAY;
00101 }
00102 
00103 bool write_build_config::process_version_parts(const istring &symbol,
00104     const istring &value)
00105 {
00106   if (symbol == "major")
00107     { _ver->set_component(version::MAJOR, value); return true; }
00108   if (symbol == "minor")
00109     { _ver->set_component(version::MINOR, value); return true; }
00110   if (symbol == "revision")
00111     { _ver->set_component(version::REVISION, value); return true; }
00112   if (symbol == "build")
00113     { _ver->set_component(version::BUILD, value); return true; }
00114   return false;
00115 }
00116 
00117 bool write_build_config::check_nesting(const istring &to_check)
00118 {
00119   if (to_check.compare(IFEQ_STATEMENT, 0, 0, int(strlen(IFEQ_STATEMENT)))
00120       || to_check.compare(IFNEQ_STATEMENT, 0, 0,
00121           int(strlen(IFNEQ_STATEMENT)))) {
00122     _nesting++;
00123     return true;
00124   }
00125   if (to_check.compare(ENDIF_STATEMENT, 0, 0, int(strlen(ENDIF_STATEMENT)))) {
00126     _nesting--;
00127     return true;
00128   }
00129   return false;
00130 }
00131 
00132 outcome write_build_config::output_decorated_macro(const istring &symbol_in,
00133     const istring &value, istring &cfg_accumulator, istring &ver_accumulator)
00134 {
00135   // make sure we catch any conditionals.
00136   if (check_nesting(symbol_in))
00137     ADD_COMMENT_RETURN(symbol_in, value);
00138   // toss out any exclusions.
00139   if (exclusions().member(symbol_in))
00140     ADD_COMMENT_RETURN(symbol_in, value);
00141   if (symbol_in.contains("\"") || value.contains("\""))
00142     ADD_COMMENT_RETURN(symbol_in, value);
00143   if (symbol_in[0] == '[')
00144     ADD_COMMENT_RETURN(symbol_in, value);
00145   if (_nesting)
00146     ADD_COMMENT_RETURN(symbol_in, value);
00147   // switch the output stream based on whether its a version component or not.
00148   istring *the_accumulator = &cfg_accumulator;
00149   if (process_version_parts(symbol_in, value)) {
00150     the_accumulator = &ver_accumulator;
00151   }
00152   // add a special tag so that we won't be colliding with other names.
00153   istring symbol = istring("__build_") + symbol_in;
00154   return output_macro(symbol, value, *the_accumulator);
00155 }
00156 
00157 outcome write_build_config::output_definition_macro
00158     (const istring &embedded_value, istring &accumulator)
00159 {
00160   FUNCDEF("output_definition_macro");
00161 //LOG(istring("into output def with: ") + embedded_value);
00162   tokenizer t;
00163   t.parse(embedded_value);
00164   if (!t.symbols())
00165     ADD_COMMENT_RETURN("bad definition", embedded_value);
00166   if (exclusions().member(t.table().name(0)))
00167     ADD_COMMENT_RETURN(t.table().name(0), t.table()[0]);
00168   if (_nesting)
00169     ADD_COMMENT_RETURN(t.table().name(0), t.table()[0]);
00170   return output_macro(t.table().name(0), t.table()[0], accumulator);
00171 }
00172 
00173 bool write_build_config::write_output_file(const istring &filename,
00174     const istring &new_contents)
00175 {
00176   FUNCDEF("write_output_file");
00177   // now read the soon-to-be output file so we can check its current state.
00178   bool write_header = true;
00179   byte_filer check_header(filename, "rb");
00180   if (check_header.good()) {
00181     byte_array file_contents;
00182     int read = check_header.read(file_contents, MAX_HEADER_FILE);
00183 if (read < 1) LOG("why is existing header contentless?");
00184     if (read > 0) {
00185       istring found(istring::UNTERMINATED, (char *)file_contents.observe(),
00186           read);
00187 //LOG(istring("got existing content:\n-----\n") + found + "\n-----\n");
00188 //LOG(istring("new_content has:\n-----\n") + new_contents + "\n-----\n");
00189       if (found == new_contents) {
00190         write_header = false;
00191       }
00192     }
00193   }
00194   // writing only occurs when we know that the build configurations have
00195   // changed.  if the file is the same, we definitely don't want to write
00196   // it because pretty much all the other files depend on it.
00197   if (write_header) {
00198     // we actually want to blast out a new file.
00199     byte_filer build_header(filename, "wb");
00200     if (!build_header.good())
00201       non_continuable_error(static_class_name(), func, istring("failed to create "
00202           "build header file in ") + build_header.filename());
00203     build_header.write(new_contents);
00204     LOG(istring(static_class_name()) + ": wrote config to "
00205         + build_header.filename());
00206   } else {
00207     // nothing has changed.
00208 //    LOG(istring(static_class_name()) + ": config already up to date in "
00209 //        + filename);
00210   }
00211   return true;
00212 }
00213 
00214 int write_build_config::execute()
00215 {
00216   FUNCDEF("execute");
00217   SET_DEFAULT_CONSOLE_LOGGER;  // override the file_logger from app_shell.
00218 
00219   // find our build ini file.
00220   istring repodir = portable::env_string("REPOSITORY_DIR");
00221 
00222   // the below code should never be needed for a properly configured build.
00223 #ifdef __WIN32__
00224   if (!repodir) repodir = "l:";
00225 #else  // unix and other locations.
00226   if (!repodir)
00227     repodir = portable::env_string("HOME") + "/hoople";
00228 #endif
00229 
00230   istring fname = repodir + "/build.ini";
00231     // where we seek out our build settings.
00232 
00233   // these are very specific paths, but they really are where we expect to
00234   // see the headers.
00235   istring cfg_header_filename = repodir + "/source/lib_src/library/"
00236       "__build_configuration.h";
00237   istring ver_header_filename = repodir + "/source/lib_src/library/"
00238       "__build_version.h";
00239 
00240   // open the ini file for reading.
00241   byte_filer ini(fname, "r");
00242   if (!ini.good())
00243     non_continuable_error(static_class_name(), func, istring("failed to open "
00244         "build.ini file for reading at ") + ini.filename());
00245 
00246   // now we build strings that represents the output files we want to create.
00247   istring cfg_accumulator;
00248   istring ver_accumulator;
00249 
00250   // odd indentation below comes from the strings being c++ code that will be
00251   // written to a file.
00252   cfg_accumulator += "\
00253 #ifndef BUILD_SYSTEM_CONFIGURATION\n\
00254 #define BUILD_SYSTEM_CONFIGURATION\n\n\
00255   // This file provides all of the code flags which were used when this\n\
00256   // build was generated.  Some of the items in build.ini have been stripped\n\
00257   // out because they are not used.\n\n";
00258   ver_accumulator += "\
00259 #ifndef BUILD_VERSION_CONFIGURATION\n\
00260 #define BUILD_VERSION_CONFIGURATION\n\n\
00261   // This file provides the version macros for this particular build.\n\n";
00262 
00263   // iterate through the entries we read in earlier and generate our header.
00264   istring symbol, value;
00265   istring buffer;
00266   while (!ini.eof()) {
00267     int chars = ini.getline(buffer, MAX_LINE_SIZE);
00268     if (!chars) continue;  // hmmm.
00269     
00270     tokenizer t;
00271     t.parse(buffer);
00272     if (!t.symbols()) continue;  // not so good.
00273 
00274     // pull out the first pair we found and try to parse it.
00275     symbol = t.table().name(0);
00276     value = t.table()[0];
00277     symbol.strip_spaces(istring::FROM_BOTH_SIDES);
00278 
00279     // clean out + characters that can come from += declarations.
00280     while ( (symbol[symbol.end()] == '+') || (symbol[symbol.end()] == ':') ) {
00281       symbol.zap(symbol.end(), symbol.end());
00282       symbol.strip_spaces(istring::FROM_END);
00283     }
00284 
00285     if (symbol[0] == '#') continue;  // toss out any comments.
00286 
00287     if (!symbol) continue;  // seems like that one didn't work out so well.
00288 
00289     if (symbol.compare(EXPORT_STATEMENT, 0, 0, int(strlen(EXPORT_STATEMENT)))) {
00290       // clean out export statements in front of our variables.
00291       symbol.zap(0, int(strlen(EXPORT_STATEMENT) - 1));
00292     }
00293 
00294     // check for a make-style macro definition.
00295     if (symbol.compare(DEFINITIONS_STATEMENT, 0, 0,
00296         int(strlen(DEFINITIONS_STATEMENT)))) {
00297       // found a macro definition.  write that up after pulling the real
00298       // contents out.
00299       output_definition_macro(value, cfg_accumulator);
00300     } else {
00301       // this one is hopefully a very tasty specialized macro.  we will
00302       // show it with added text to make it unique.
00303       output_decorated_macro(symbol, value, cfg_accumulator, ver_accumulator);
00304     }
00305   }
00306 
00307   // write some calculated macros now.
00308   ver_accumulator += "\n";
00309   ver_accumulator += "  // calculated macros are dropped in here.\n\n";
00310 
00311   // we write our version in a couple forms.  hopefully we accumulated it.
00312 
00313   // this one is the same as the file version currently (below), but may go to
00314   // a different version at some point.
00315   ver_accumulator += "  #define __build_SYSTEM_VERSION \"";
00316   ver_accumulator += _ver->flex_text_form();
00317   ver_accumulator += "\"\n\n";
00318 
00319   // we drop in the version as numbers also, since the version RC wants that.
00320   ver_accumulator += "  #define __build_FILE_VERSION_COMMAS ";
00321   ver_accumulator += _ver->flex_text_form(version::COMMAS);
00322   ver_accumulator += "\n";
00323   // another form of the file version for dotted notation.
00324   ver_accumulator += "  #define __build_FILE_VERSION \"";
00325   ver_accumulator += _ver->flex_text_form();
00326   ver_accumulator += "\"\n";
00327 
00328   // product version is just the first two parts.
00329   _ver->set_component(version::REVISION, "0");
00330   _ver->set_component(version::BUILD, "0");
00331   // product version as a list of numbers.
00332   ver_accumulator += "  #define __build_PRODUCT_VERSION_COMMAS ";
00333   ver_accumulator += _ver->flex_text_form(version::COMMAS);
00334   ver_accumulator += "\n";
00335   // another form of the product version for use as a string.
00336   ver_accumulator += "  #define __build_PRODUCT_VERSION \"";
00337   ver_accumulator += _ver->flex_text_form(version::DOTS, version::MINOR);
00338   ver_accumulator += "\"\n";
00339 
00340   // write a blob of comments at the end with what we didn't use.
00341   cfg_accumulator += "\n";
00342   cfg_accumulator += "/*\n";
00343   cfg_accumulator += "These settings were not used:\n";
00344   cfg_accumulator += *_end_matter;
00345   cfg_accumulator += "*/\n";
00346   cfg_accumulator += "\n";
00347   cfg_accumulator += "#endif /* outer guard */\n\n";
00348 
00349   // finish up the version file also.
00350   ver_accumulator += "\n";
00351   ver_accumulator += "#endif /* outer guard */\n\n";
00352 
00353   if (!write_output_file(cfg_header_filename, cfg_accumulator)) {
00354     LOG(istring("failed writing output file ") + cfg_header_filename);
00355   }
00356   if (!write_output_file(ver_header_filename, ver_accumulator)) {
00357     LOG(istring("failed writing output file ") + ver_header_filename);
00358   }
00359 
00360   return 0;
00361 }
00362 
00363 HOOPLE_MAIN(write_build_config, )
00364 
00365 #ifdef __BUILD_STATIC_APPLICATION__
00366   // static dependencies found by buildor_gen_deps.sh:
00367   #include <basis/array.cpp>
00368   #include <basis/byte_array.cpp>
00369   #include <basis/callstack_tracker.cpp>
00370   #include <basis/chaos.cpp>
00371   #include <basis/convert_utf.cpp>
00372   #include <basis/definitions.cpp>
00373   #include <basis/earth_time.cpp>
00374   #include <basis/guards.cpp>
00375   #include <basis/istring.cpp>
00376   #include <basis/log_base.cpp>
00377   #include <basis/memory_checker.cpp>
00378   #include <basis/mutex.cpp>
00379   #include <basis/object_base.cpp>
00380   #include <basis/outcome.cpp>
00381   #include <basis/packable.cpp>
00382   #include <basis/portable.cpp>
00383   #include <basis/sequence.cpp>
00384   #include <basis/set.cpp>
00385   #include <basis/utility.cpp>
00386   #include <basis/version_checker.cpp>
00387   #include <basis/version_record.cpp>
00388   #include <data_struct/amorph.cpp>
00389   #include <data_struct/bit_vector.cpp>
00390   #include <data_struct/byte_hasher.cpp>
00391   #include <data_struct/configurator.cpp>
00392   #include <data_struct/hash_table.cpp>
00393   #include <data_struct/pointer_hash.cpp>
00394   #include <data_struct/stack.cpp>
00395   #include <data_struct/static_memory_gremlin.cpp>
00396   #include <data_struct/string_hash.cpp>
00397   #include <data_struct/string_hasher.cpp>
00398   #include <data_struct/string_table.cpp>
00399   #include <data_struct/symbol_table.cpp>
00400   #include <data_struct/table_configurator.cpp>
00401   #include <loggers/console_logger.cpp>
00402   #include <loggers/file_logger.cpp>
00403   #include <loggers/locked_logger.cpp>
00404   #include <loggers/null_logger.cpp>
00405   #include <loggers/program_wide_logger.cpp>
00406   #include <opsystem/application_base.cpp>
00407   #include <opsystem/application_shell.cpp>
00408   #include <opsystem/byte_filer.cpp>
00409   #include <opsystem/command_line.cpp>
00410   #include <opsystem/critical_events.cpp>
00411   #include <opsystem/directory.cpp>
00412   #include <opsystem/filename.cpp>
00413   #include <opsystem/ini_config.cpp>
00414   #include <opsystem/ini_parser.cpp>
00415   #include <opsystem/path_configuration.cpp>
00416   #include <opsystem/rendezvous.cpp>
00417   #include <opsystem/version_ini.cpp>
00418   #include <textual/byte_format.cpp>
00419   #include <textual/parser_bits.cpp>
00420   #include <textual/string_manipulation.cpp>
00421   #include <textual/tokenizer.cpp>
00422 #endif // __BUILD_STATIC_APPLICATION__
00423 

Generated on Fri Nov 21 04:29:07 2008 for HOOPLE Libraries by  doxygen 1.5.1