super tasty version fixes
[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 //
223       astring versions_directory = environment::get("FEISTY_MEOW_GENERATED_STORE");
224      // we keep our version files one level below the top of the generated store.
225       versions_directory += "/versions";
226
227       version_ini::one_stop_version_stamp(current.path() + "/" + current_file,
228           versions_directory, global_build_ini, true);
229     }
230   }
231   return true;
232 }
233
234 #undef static_class_name
235
236 ////////////////////////////////////////////////////////////////////////////
237
238 bool vsts_version_fixer::perform_version_stamping(const filename &start_name)
239 {
240   FUNCDEF("perform_version_stamping");
241   directory start(start_name);
242   return spider_directory(start, stamping_spider);
243 }
244
245 ////////////////////////////////////////////////////////////////////////////
246
247 void vsts_version_fixer::remove_confusing_files()
248 {
249   using namespace basis;
250   // clean out a few directories that show up in the source tree from c#
251   // projects compilation.  c# projects always rebuild every time anyways,
252   // so this doesn't lose us any compilation time.  the only thing c#
253   // projects don't ever seem to rebuild is their version resource, unless
254   // they're forced to totally recompile like we cause below.
255   string_array source_file_whacks;  // none right now.
256   string_array source_dir_whacks;
257   source_dir_whacks += "obj";
258   source_dir_whacks += "Debug";
259   source_dir_whacks += "Release";
260   source_dir_whacks += "bin";
261   source_dir_whacks += "temp_build";
262   directory repo_source(environment::get("FEISTY_MEOW_APEX") + "/source");
263   whack_in_subdirs(repo_source, source_file_whacks, source_dir_whacks);
264   directory libra_src(environment::get("FEISTY_MEOW_APEX") + "/libraries");
265   whack_in_subdirs(libra_src, source_file_whacks, source_dir_whacks);
266   directory produ_src(environment::get("FEISTY_MEOW_APEX") + "/products");
267   whack_in_subdirs(produ_src, source_file_whacks, source_dir_whacks);
268
269 /* this never helped.
270   // clean out a variety of bad files in the objects hierarchy.
271   // currently this is just the generated RES files which we have seen cause
272   // vsts to think apps and dlls are up to date when they are actually not.
273   directory repo_objects(environment::get("FEISTY_MEOW_APEX"));
274   string_array objects_file_whacks;
275   objects_file_whacks += ".res";
276   string_array objects_dir_whacks;  // none right now.
277   whack_in_subdirs(repo_objects, objects_file_whacks, objects_dir_whacks);
278 */
279 }
280
281 int vsts_version_fixer::execute()
282 {
283   FUNCDEF("execute");
284   using namespace basis;
285   log(time_stamp::notarize(true) + "vsts_version_fixer started.", ALWAYS_PRINT);
286
287   remove_confusing_files();
288
289   astring repo_dir = environment::get("FEISTY_MEOW_APEX");
290
291   // figure out which build parameter file to use.
292   global_build_ini = "";
293   astring parmfile = environment::get("BUILD_PARAMETER_FILE");
294   if (parmfile.t()) {
295     global_build_ini = parmfile;
296 LOG(astring("found parm variable ") + parmfile);
297   } else {
298     // they didn't specify the file.  argh.
299     global_build_ini = repo_dir + "/production/feisty_meow_config.ini";
300     if (!filename(global_build_ini).exists()) {
301 LOG(astring("guess not found: ") + global_build_ini);
302       LOG("cannot locate the build configuration file.");
303       return 3; 
304     }
305   }
306
307   // now stamp versions on everything we can find.
308   filename repo_source = repo_dir + "/../../libraries";
309   if (!repo_source.exists()) {
310     repo_source = repo_dir + "/source";
311     if (!repo_source.exists()) {
312       LOG("cannot locate the main library source location.");
313       return 3; 
314     }
315   }
316 LOG(astring("chose source dir as ") + repo_source);
317   perform_version_stamping(repo_source);
318
319   filename repo_apps = repo_dir + "/../../products";
320   if (repo_apps.exists()) {
321     perform_version_stamping(repo_apps);
322   }
323   log(time_stamp::notarize(true) + "vsts_version_fixer finished.", ALWAYS_PRINT);
324   return 0;
325 }
326
327 #ifdef __BUILD_STATIC_APPLICATION__
328   // static dependencies found by buildor_gen_deps.sh:
329   #include <application/application_shell.cpp>
330   #include <application/command_line.cpp>
331   #include <application/windoze_helper.cpp>
332   #include <basis/astring.cpp>
333   #include <basis/common_outcomes.cpp>
334   #include <basis/environment.cpp>
335   #include <basis/guards.cpp>
336   #include <basis/mutex.cpp>
337   #include <basis/utf_conversion.cpp>
338   #include <configuration/application_configuration.cpp>
339   #include <configuration/configurator.cpp>
340   #include <configuration/ini_configurator.cpp>
341   #include <configuration/ini_parser.cpp>
342   #include <configuration/table_configurator.cpp>
343   #include <configuration/variable_tokenizer.cpp>
344   #include <filesystem/byte_filer.cpp>
345   #include <filesystem/directory.cpp>
346   #include <filesystem/filename.cpp>
347   #include <loggers/combo_logger.cpp>
348   #include <loggers/console_logger.cpp>
349   #include <loggers/critical_events.cpp>
350   #include <loggers/file_logger.cpp>
351   #include <loggers/program_wide_logger.cpp>
352   #include <processes/launch_process.cpp>
353   #include <structures/bit_vector.cpp>
354   #include <structures/checksums.cpp>
355   #include <structures/object_packers.cpp>
356   #include <structures/static_memory_gremlin.cpp>
357   #include <structures/string_hasher.cpp>
358   #include <structures/string_table.cpp>
359   #include <structures/version_record.cpp>
360   #include <textual/byte_formatter.cpp>
361   #include <textual/parser_bits.cpp>
362   #include <textual/string_manipulation.cpp>
363   #include <timely/earth_time.cpp>
364   #include <timely/time_control.cpp>
365   #include <timely/time_stamp.cpp>
366   #include <versions/version_ini.cpp>
367 #endif // __BUILD_STATIC_APPLICATION__
368