bef8fcbafaf727d3b5f0b215280e4c54f8181722
[feisty_meow.git] / nucleus / tools / clam_tools / vsts_version_fixer.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : vsts_version_fixer                                                *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 2008-$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 <application/application_shell.h>
16 #include <application/command_line.h>
17 #include <application/hoople_main.h>
18 #include <application/windoze_helper.h>
19 #include <basis/astring.h>
20 #include <basis/environment.h>
21 #include <basis/functions.h>
22 #include <filesystem/byte_filer.h>
23 #include <filesystem/directory.h>
24 #include <filesystem/filename.h>
25 #include <processes/launch_process.h>
26 #include <structures/static_memory_gremlin.h>
27 #include <structures/string_array.h>
28 #include <timely/time_stamp.h>
29 #include <versions/version_ini.h>
30
31 #undef LOG
32 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
33 #undef BASE_LOG
34 #define BASE_LOG(s) program_wide_logger::get().log(s, ALWAYS_PRINT)
35
36 using namespace application;
37 //using namespace basis;
38 using namespace filesystem;
39 using namespace loggers;
40 using namespace processes;
41 using namespace structures;
42 using namespace timely;
43 using namespace versions;
44
45 //#define DEBUG_VSTS_VERSION_FIXER
46   // uncomment for noisy version.
47
48 ////////////////////////////////////////////////////////////////////////////
49
50 class vsts_version_fixer : public application::application_shell
51 {
52 public:
53   vsts_version_fixer() : application_shell() {}
54   virtual ~vsts_version_fixer() {}
55
56   virtual int execute();
57
58   DEFINE_CLASS_NAME("vsts_version_fixer");
59
60   void remove_confusing_files();
61     //!< tosses out the generated files that confuse ms devstudio.
62
63 //move these
64   typedef bool spider_method(const directory &current);
65     //!< prototype for functions that are called during directory spidering.
66     /*!< this function should do whatever work is needed on the items in
67     that "current" directory.  true should be returned by this method when
68     the traversal of the directory is still desired.  if there is a reason
69     to stop traversing the directory hierarchy, then it should return false. */
70
71 //hmmm: support postfix and in order also.
72 //hmmm: support reporting where the spidering stopped.
73   bool spider_directory(directory start, spider_method to_invoke);
74     //!< traverses hierarchy "start" in prefix order while calling "to_invoke".
75     /*!< true is returned if all invoked spider methods returned true.
76     otherwise, false is returned. */
77 //move those
78
79   bool perform_version_stamping(const filename &start_name);
80     //!< finds all version ini files and applies stamps using them.
81
82   void whack_in_subdirs(const directory &start,
83       const string_array &file_whacks, const string_array &dir_whacks);
84     //!< recursively cleans all items found in "file_whacks" and "dir_whacks".
85     /*!< "file_whacks" is a list of file suffixes to whack.  for example, to
86     remove all files matching a pattern *.exe, pass in just ".exe" in the
87     "file_whacks".  the "dir_whacks" list is a list of directories to
88     completely obliterate where found. */
89 };
90
91 HOOPLE_MAIN(vsts_version_fixer, )
92
93 ////////////////////////////////////////////////////////////////////////////
94
95 //hmmm: move to a useful place; maybe even in directory class?
96 bool vsts_version_fixer::spider_directory(directory start,
97     spider_method to_invoke)
98 {
99   FUNCDEF("spider_directory");
100
101   using namespace basis;
102
103 //LOG(astring("spider_directory: ") + start.path());
104   // call our method on this directory first.  this ensures that we have
105   // dealt with it before we spider off elsewhere.
106   bool ret = to_invoke(start);
107   if (!ret) return false;  // bail.
108
109   // now let's look at the subdirectories.  we'll recurse on all of them in
110   // the order listed.
111   const string_array &dirs = start.directories();
112 //LOG(astring("dirs found to spider: ") + dirs.text_form());
113   for (int dir_indy = 0; dir_indy < dirs.length(); dir_indy++) {
114     const astring &current_dir = dirs[dir_indy];
115 //LOG(astring("currdir into ") + current_dir);
116     if (current_dir.equal_to(".svn")) continue;  // skip this.
117     if (current_dir.equal_to("CVS")) continue;  // skip this also.
118     directory new_dir(start.path() + "/" + current_dir, start.pattern().observe());
119     bool ret = spider_directory(new_dir, to_invoke);
120     if (!ret) return false;  // bail from subdir issue.
121   }
122   // if we made it to here, everything was groovy.
123   return true;
124 }
125
126 ////////////////////////////////////////////////////////////////////////////
127
128 #define static_class_name() "vsts_version_fixer"
129
130 // global variables used to communicate with whacking_spider.
131 string_array global_file_whacks;
132 string_array global_dir_whacks;
133
134 bool whacking_spider(const directory &current)
135 {
136   FUNCDEF("whacking_spider");
137   using namespace basis;
138 //LOG(astring("whacking_spider: ") + current.path());
139   // iterate across the files in the directory and check for evil ones.
140   const string_array &files = current.files();
141   for (int file_indy = 0; file_indy < files.length(); file_indy++) {
142     const astring &current_file = files[file_indy];
143 //LOG(astring("currfile ") + current_file);
144     // now iterate across our pattern list to see if this thing is
145     // one of the offending files.
146     for (int pat_indy = 0; pat_indy < global_file_whacks.length(); pat_indy++) {
147 //LOG(astring("currpat ") + global_file_whacks[pat_indy]);
148       if (current_file.iends(global_file_whacks[pat_indy])) {
149         filename goner(current.path() + "/" + current_file);
150         BASE_LOG(astring("whack file: ") + goner.raw());
151         goner.unlink();
152         break;  // stop looking at the pattern list for matches.
153       }
154     }
155   }
156
157   // okay, now that we've cleaned out those files, let's look at the
158   // subdirectories.
159   const string_array &dirs = current.directories();
160   for (int dir_indy = 0; dir_indy < dirs.length(); dir_indy++) {
161     const astring &current_dir = dirs[dir_indy];
162 //LOG(astring("currdir ") + current_dir);
163     for (int pat_indy = 0; pat_indy < global_dir_whacks.length(); pat_indy++) {
164       if (current_dir.iequals(global_dir_whacks[pat_indy])) {
165         filename goner(current.path() + "/" + current_dir);
166         BASE_LOG(astring("whack dir: ") + goner.raw());
167 //hmmm: plug in recursive delete here instead.
168 basis::un_int kid;
169 launch_process::run("rm", astring("-rf ") + goner.raw(), launch_process::AWAIT_APP_EXIT, kid);
170         break;  // skip remainder of patterns for this dir.
171       }
172     }
173   }
174   return true;
175 }
176
177 #undef static_class_name
178
179 ////////////////////////////////////////////////////////////////////////////
180
181 void vsts_version_fixer::whack_in_subdirs(const directory &start,
182     const string_array &file_whacks, const string_array &dir_whacks)
183 {
184   FUNCDEF("whack_in_subdirs");
185   using namespace basis;
186
187   // save the lists so the spider method can see them.
188   // note that this approach with a global variable would be bad if there
189   // were concurrent invocations of the spidering, but we're not doing
190   // that here.
191   global_file_whacks = file_whacks;
192   global_dir_whacks = dir_whacks;
193
194   bool worked = spider_directory(start, whacking_spider);
195   if (!worked) {
196     LOG(astring("spidering of ") + start.path() + " failed for some reason.");
197   }
198 }
199
200 ////////////////////////////////////////////////////////////////////////////
201
202 #define static_class_name() "vsts_version_fixer"
203
204 basis::astring global_build_ini;
205
206 bool stamping_spider(const directory &current)
207 {
208   FUNCDEF("stamping_spider");
209   using namespace basis;
210 //LOG(astring("stamping_spider: ") + current.path());
211
212   const string_array &files = current.files();
213   for (int file_indy = 0; file_indy < files.length(); file_indy++) {
214     const astring &current_file = files[file_indy];
215 //LOG(astring("currfile ") + current_file);
216     // we won't process the "core_version.ini" file, which is a special
217     // case that is somewhat well known as not being a file used (by us)
218     // for dlls.
219     if (current_file.ends("version.ini")
220         && !current_file.iequals("core_version.ini") ) {
221 //LOG(astring("found ver file: ") + current.path() + "/" + current_file);
222       version_ini::one_stop_version_stamp(current.path() + "/" + current_file,
223           global_build_ini, true);
224     }
225   }
226   return true;
227 }
228
229 #undef static_class_name
230
231 ////////////////////////////////////////////////////////////////////////////
232
233 bool vsts_version_fixer::perform_version_stamping(const filename &start_name)
234 {
235   FUNCDEF("perform_version_stamping");
236   directory start(start_name);
237   return spider_directory(start, stamping_spider);
238 }
239
240 ////////////////////////////////////////////////////////////////////////////
241
242 void vsts_version_fixer::remove_confusing_files()
243 {
244   using namespace basis;
245   // clean out a few directories that show up in the source tree from c#
246   // projects compilation.  c# projects always rebuild every time anyways,
247   // so this doesn't lose us any compilation time.  the only thing c#
248   // projects don't ever seem to rebuild is their version resource, unless
249   // they're forced to totally recompile like we cause below.
250   string_array source_file_whacks;  // none right now.
251   string_array source_dir_whacks;
252   source_dir_whacks += "obj";
253   source_dir_whacks += "Debug";
254   source_dir_whacks += "Release";
255   source_dir_whacks += "bin";
256   source_dir_whacks += "temp_build";
257   directory repo_source(environment::get("FEISTY_MEOW_APEX") + "/source");
258   whack_in_subdirs(repo_source, source_file_whacks, source_dir_whacks);
259   directory libra_src(environment::get("FEISTY_MEOW_APEX") + "/libraries");
260   whack_in_subdirs(libra_src, source_file_whacks, source_dir_whacks);
261   directory produ_src(environment::get("FEISTY_MEOW_APEX") + "/products");
262   whack_in_subdirs(produ_src, source_file_whacks, source_dir_whacks);
263
264 /* this never helped.
265   // clean out a variety of bad files in the objects hierarchy.
266   // currently this is just the generated RES files which we have seen cause
267   // vsts to think apps and dlls are up to date when they are actually not.
268   directory repo_objects(environment::get("FEISTY_MEOW_APEX"));
269   string_array objects_file_whacks;
270   objects_file_whacks += ".res";
271   string_array objects_dir_whacks;  // none right now.
272   whack_in_subdirs(repo_objects, objects_file_whacks, objects_dir_whacks);
273 */
274 }
275
276 int vsts_version_fixer::execute()
277 {
278   FUNCDEF("execute");
279   using namespace basis;
280   log(time_stamp::notarize(true) + "vsts_version_fixer started.", ALWAYS_PRINT);
281
282   remove_confusing_files();
283
284   astring repo_dir = environment::get("FEISTY_MEOW_APEX");
285
286   // figure out which build parameter file to use.
287   global_build_ini = "";
288   astring parmfile = environment::get("BUILD_PARAMETER_FILE");
289   if (parmfile.t()) {
290     global_build_ini = parmfile;
291 LOG(astring("found parm variable ") + parmfile);
292   } else {
293     // they didn't specify the file.  argh.
294     global_build_ini = repo_dir + "/production/feisty_meow_config.ini";
295     if (!filename(global_build_ini).exists()) {
296 LOG(astring("guess not found: ") + global_build_ini);
297       LOG("cannot locate the build configuration file.");
298       return 3; 
299     }
300   }
301
302   // now stamp versions on everything we can find.
303   filename repo_source = repo_dir + "/../../libraries";
304   if (!repo_source.exists()) {
305     repo_source = repo_dir + "/source";
306     if (!repo_source.exists()) {
307       LOG("cannot locate the main library source location.");
308       return 3; 
309     }
310   }
311 LOG(astring("chose source dir as ") + repo_source);
312   perform_version_stamping(repo_source);
313
314   filename repo_apps = repo_dir + "/../../products";
315   if (repo_apps.exists()) {
316     perform_version_stamping(repo_apps);
317   }
318   log(time_stamp::notarize(true) + "vsts_version_fixer finished.", ALWAYS_PRINT);
319   return 0;
320 }
321
322 #ifdef __BUILD_STATIC_APPLICATION__
323   // static dependencies found by buildor_gen_deps.sh:
324   #include <application/application_shell.cpp>
325   #include <application/command_line.cpp>
326   #include <application/windoze_helper.cpp>
327   #include <basis/astring.cpp>
328   #include <basis/common_outcomes.cpp>
329   #include <basis/environment.cpp>
330   #include <basis/guards.cpp>
331   #include <basis/mutex.cpp>
332   #include <basis/utf_conversion.cpp>
333   #include <configuration/application_configuration.cpp>
334   #include <configuration/configurator.cpp>
335   #include <configuration/ini_configurator.cpp>
336   #include <configuration/ini_parser.cpp>
337   #include <configuration/table_configurator.cpp>
338   #include <configuration/variable_tokenizer.cpp>
339   #include <filesystem/byte_filer.cpp>
340   #include <filesystem/directory.cpp>
341   #include <filesystem/filename.cpp>
342   #include <loggers/combo_logger.cpp>
343   #include <loggers/console_logger.cpp>
344   #include <loggers/critical_events.cpp>
345   #include <loggers/file_logger.cpp>
346   #include <loggers/program_wide_logger.cpp>
347   #include <processes/launch_process.cpp>
348   #include <structures/bit_vector.cpp>
349   #include <structures/checksums.cpp>
350   #include <structures/object_packers.cpp>
351   #include <structures/static_memory_gremlin.cpp>
352   #include <structures/string_hasher.cpp>
353   #include <structures/string_table.cpp>
354   #include <structures/version_record.cpp>
355   #include <textual/byte_formatter.cpp>
356   #include <textual/parser_bits.cpp>
357   #include <textual/string_manipulation.cpp>
358   #include <timely/earth_time.cpp>
359   #include <timely/time_control.cpp>
360   #include <timely/time_stamp.cpp>
361   #include <versions/version_ini.cpp>
362 #endif // __BUILD_STATIC_APPLICATION__
363