srt_ntide_project_file.py
Go to the documentation of this file.
1 #
2 # ide_project_file.py
3 #
4 # These functions create a project file given an ide_target object. The project files
5 # are all template files, so we first fill in a dictionary with all the possible values
6 # and then hand it off to the template translator to do the dirty work.
7 #
8 # Unforunately for us, DevStudio only handles about 2K worth of characters in
9 # all of its environment vars and other things. Thus, we have to write a seperate
10 # file with all the includes in it to keep the command line short enough. This file
11 # is also inserted in the project so the user can get at it easily.
12 #
13 # Created November 1997 Gordon Watts (Brown)
14 #
15 
16 import srt_file_template
17 import srt_path_util
18 import srt_ntide_target
19 import srt_ntide_list_set
20 import regsub
21 import regex
22 import os
23 
24 dir_debug_prefix = "Debug_"
25 dir_release_prefix = "Release_"
26 
27 #
28 # create_project_file
29 #
30 # Create the .dsp file for the project
31 #
32 def create_project_file (project, target_list, newrel_dir):
33  project_dsp = open (project.get_project_file(), "w")
34 
35  #
36  # Figure out what the name of the template file is. We will use this
37  # as the basis for our project.
38  #
39 
40  template_name = "srt_ntide_project_" + project.get_target_type() + ".template"
41  template_filename = srt_path_util.find_file_in_PATH (template_name)
42  if template_filename == "":
43  raise "Could not find template file " + template_name + " in PATH."
44 
45  #
46  # We have to create the dictionary of all the stuff we know about that
47  # will be put into the output file. Sigh.
48  #
49 
50  project_dict = {}
51 
52  # The project name
53  project_dict["project_name"] = project.get_project_ide_name()
54  print project_dict["project_name"]
55 
56  # The target type
57  t_type = project.get_target_type()
58  if t_type == "lib":
59  t_build = '# TARGTYPE "Win32 (x86) Static Library" 0x0104'
60  elif (t_type == 'exe') or (t_type == ''):
61  t_build = '# TARGTYPE "Win32 (x86) Console Application" 0x0103'
62  project_dict["project_type"] = t_build
63 
64  # The cygwin path
65 
66  project_dict["cygwin_path"] = os.environ["CYGNUS_BIN"]
67 
68  # The working directories
69 
70  project_dict["working_dir_debug"] = dir_debug_prefix + project.get_target_root_file()
71  project_dict["working_dir_release"] = dir_release_prefix + project.get_target_root_file()
72 
73  # The name of the destination file
74 
75  project_dict["target_filename"] = project.get_target_name_root()
76  project_dict["target_name"] = project.get_target_root_file()
77 
78  f_target_path = project.get_target_name()
79  project_dict["final_target"] = srt_path_util.get_relative_path (f_target_path, project.get_project_file_dir())
80  project_dict["final_target_unix"] = srt_path_util.path_to_unix(project_dict["final_target"])
81  (project_dict["final_target_dir"], bogus) = os.path.split (project_dict["final_target"])
82 
83  # Compiler flags, includes, and other per-source file things
84  source_targets = project.get_non_project_targets (target_list)
85 
86  source_options_list = {}
87  include_dir_list = {}
88  macro_define_list = {}
89  link_library_list = srt_ntide_list_set.srt_ntide_list_set()
90  link_lib_path_list = srt_ntide_list_set.srt_ntide_list_set()
91  link_options_list = srt_ntide_list_set.srt_ntide_list_set()
92  header_file_list = srt_ntide_list_set.srt_ntide_list_set()
93 
94  for target_type in srt_ntide_target.source_file_classes.keys():
95  source_options_list[target_type] = srt_ntide_list_set.srt_ntide_list_set()
96  include_dir_list[target_type] = srt_ntide_list_set.srt_ntide_list_set()
97  macro_define_list[target_type] = srt_ntide_list_set.srt_ntide_list_set()
98 
99  for a_target in source_targets:
100 
101  #
102  # Get the source file from the target, and its class
103  #
104 
105  a_source_file = target_list[a_target].get_target_source_file_list()[0]
106  target_class = srt_ntide_target.get_file_class(a_source_file)
107  if target_class == "":
108  raise "Error file of type " + target_class + " is not a source class for target " + a_target + "!"
109 
110  #
111  # Find the common options, include dirs, and macro defines it has
112  #
113 
114  source_options_list[target_class].intersection (target_list[a_target].get_options_list())
115  include_dir_list[target_class].intersection(target_list[a_target].get_include_list())
116  macro_define_list[target_class].intersection(target_list[a_target].get_macro_define_list())
117 
118  #
119  # Accumulate any particular link libraries or the like
120  #
121 
122  link_library_list.add(target_list[a_target].get_link_libraries())
123  link_lib_path_list.add (target_list[a_target].get_library_search_paths())
124  link_options_list.add (target_list[a_target].get_link_options())
125 
126  #
127  # And any header files it has
128  #
129 
130  header_file_list.add(target_list[a_target].get_header_files())
131 
132  #
133  # Great. We have a lot of per-project info.
134  #
135  # First up are the libraries we are going to link in. Some of them are projects
136  # we are also building... don't need to put them in, as they will be in once already.
137  #
138 
139  link_library_list.add(project.get_link_libraries())
140 
141  dep_project_names = project.get_dependent_on_projects (target_list)
142 
143  link_lib_string = ""
144  for link_lib in link_library_list.get_list():
145  if link_lib not in dep_project_names:
146  if os.path.exists(link_lib):
147  rel_file = srt_path_util.get_relative_path (link_lib, project.get_project_file_dir())
148  else:
149  rel_file = link_lib
150  link_lib_string = link_lib_string + rel_file + " "
151 
152  project_dict["link_libraries"] = link_lib_string
153 
154  #
155  # The link options
156  #
157 
158  link_options_list.add(project.get_link_options())
159  link_options_string = ""
160  for link_option in link_options_list.get_list():
161  link_options_string = link_options_string + "/" + link_option + " "
162 
163  project_dict["link_options"] = link_options_string
164 
165  #
166  # Library search paths
167  #
168 
169  link_lib_path_list.add(project.get_library_search_paths())
170 
171  link_search_paths = ""
172  for search_path in link_lib_path_list.get_list():
173  link_search_paths = link_search_paths + '/libpath:"' + search_path + '" '
174  project_dict["link_search_paths"] = link_search_paths
175 
176  #
177  # Next, assemble the standard options string that we are going to need to
178  # look at. This will include stuff like macro definitions, etc. We have to
179  # do this by compiler -- or source class
180  #
181 
182  option_string = {}
183  for source_class in srt_ntide_target.source_file_classes.keys():
184  option_string[source_class] = make_option_string (source_options_list[source_class].get_list())
185 
186  option_string["cpp"] = option_string["cpp"] + "/YX /Fp.\\" + project.get_target_root_file() + ".PCH "
187 
188  #
189  # Do it for the macro definitions
190  #
191 
192  macro_string = {}
193  for source_class in srt_ntide_target.source_file_classes.keys():
194  macro_string[source_class] = make_macro_string (macro_define_list[source_class].get_list())
195 
196  #
197  # Next, do the same loop for the include paths. This list can get so long that it overflows
198  # DevStudio's ability to store it. So we write it to a file which is later read back from
199  # the project. We also insert it in the project so that the poor user can easily look at the
200  # file.
201  #
202 
203  include_path_file = {}
204  for source_class in srt_ntide_target.source_file_classes.keys():
205  include_path_file[source_class] = srt_ntide_compiler_include_file (project, newrel_dir, "include_paths_" + source_class)
206  include_handle = include_path_file[source_class].open ()
207 
208  for includes in include_dir_list[source_class].get_list():
209  try:
210  rel_file = srt_path_util.get_relative_path (includes, project.get_project_file_dir())
211  except:
212  rel_file = includes
213  include_handle.write ('/I "' + rel_file + '"\n')
214  include_handle.close ()
215 
216  #
217  # Put together the list of compiler options
218  #
219 
220  project_dict["cpp_flags_debug"] = option_string["cpp"] \
221  + macro_string["cpp"]
222 
223  project_dict["f_flags_debug"] = option_string["f"] \
224  + macro_string["f"]
225 
226  #
227  # Next, add in the correct indirect files -- these are the guys that contain the long lists
228  # of include files.
229  #
230 
231  project_dict["compiler_option_file_list"] = ""
232  for source_class in include_path_file.keys():
233  rel_inc_filename = srt_path_util.get_relative_path (include_path_file[source_class].name(),
234  project.get_project_file_dir())
235  project_dict[source_class + "_flags_debug"] = project_dict[source_class + "_flags_debug"] + \
236  '@"' + rel_inc_filename + '"'
237 
238  project_dict["compiler_option_file_list"] = project_dict["compiler_option_file_list"] \
239  + "# Begin Source File\n\nSOURCE=.\\" + rel_inc_filename + "\n# End Source File\n"
240 
241  #
242  # Currently, the release and debug flags are the same... Eventually we will
243  # have to fix this, I suppose. :(
244  #
245 
246  project_dict["cpp_flags_release"] = project_dict["cpp_flags_debug"]
247  project_dict["f_flags_release"] = project_dict["f_flags_debug"]
248 
249  #
250  # A list of the source files. Need to add in any defines, etc. that aren't
251  # in the master list...
252  #
253 
254  source_lines = ""
255 
256  t_helper = target_spec_helper ()
257  project.traverse_source_files (t_helper, target_list)
258 
259  #
260  # Now we have all the source files and their associated targets. Time to walk
261  # through them and emit the source lines to do the work.
262  #
263 
264  project_dict["compiler_special_cmd_file_list"] = ""
265  project_dict["source_file_list"] = ""
266  for file_target_pair in t_helper.get_builds():
267  (a_file, a_target) = file_target_pair
268  file_class = srt_ntide_target.get_file_class(a_file)
269 
270  #
271  # If we have fortran files, better set the fortran marker. This prevents us
272  # from emitting fortran specific info in a ide file that doesn't need it. MSVC
273  # will choke on such a file if fortran hasn't been installed.
274  #
275 
276  if file_class == "f":
277  project_dict["has_fortran_files"] = "1"
278 
279  #
280  # Next, if it has special commands associated with it, take care of the
281  # problem. Otherwise, it is a plain old guy requiring some real work. :(
282  # Make sure to make the special command files a part of the project so the poor
283  # user can get at them pretty simply.
284  #
285 
286  source_lines = ""
287  if a_target.has_special_commands():
288  (source_lines, cmd_file_r, cmd_file_d) = create_special_commands (a_file, a_target, project)
289  project_dict["compiler_special_cmd_file_list"] = project_dict["compiler_special_cmd_file_list"] \
290  + "# Begin Source File\n\nSOURCE=.\\" + cmd_file_r + "\n# End Source File\n"
291  project_dict["compiler_special_cmd_file_list"] = project_dict["compiler_special_cmd_file_list"] \
292  + "# Begin Source File\n\nSOURCE=.\\" + cmd_file_d + "\n# End Source File\n"
293  else:
294  delta_defines = macro_define_list[file_class].difference (a_target.get_macro_define_list())
295  delta_include_dir = include_dir_list[file_class].difference (a_target.get_include_list())
296  delta_source_options = source_options_list[target_class].difference (a_target.get_options_list())
297 
298  delta_define_string = make_macro_string (delta_defines)
299  delta_include_dir_string = make_include_string (delta_include_dir, project.get_project_file_dir())
300  delta_options_string = make_option_string (delta_source_options)
301 
302  new_opt_string = delta_define_string + delta_options_string + delta_include_dir_string
303 
304  rel_file = srt_path_util.get_relative_path (a_file, project.get_project_file_dir())
305  source_lines = "# Begin Source File\n\nSOURCE=.\\" + rel_file + "\n"
306 
307  if new_opt_string != "":
308  source_lines = source_lines + "# ADD CPP " + new_opt_string + "\n"
309 
310  source_lines = source_lines + "# End Source File\n"
311 
312  project_dict["source_file_list"] = project_dict["source_file_list"] + source_lines
313 
314  #
315  # The header files that this project includes
316  #
317 
318  header_file_list.add(project.get_header_files())
319  header_string = ""
320  for header in header_file_list.get_list():
321  if srt_ntide_target.get_file_class(header) == "hpp":
322  rel_file = srt_path_util.get_relative_path (header, project.get_project_file_dir())
323  source_lines = "# Begin Source File\n\nSOURCE=.\\" + rel_file + "\n"
324  source_lines = source_lines + "# End Source File\n"
325  header_string = header_string + source_lines
326 
327  project_dict["header_file_list"] = header_string
328 
329  #
330  # Great, now do it.
331  #
332 
333  srt_file_template.file_template (project_dsp, template_filename, project_dict)
334 
335  project_dsp.close()
336 
337 #############################################
338 #
339 # target_spec_helper
340 #
341 # Temp object to help out with traversing the source file list (and constructing
342 # the source files!).
343 #
345  def __init__(self):
346  self._normal_files = []
347 
348  def get_builds (self):
349  return self._normal_files
350 
351  def source_file (self, a_file, target_obj):
352  self._normal_files.append ([a_file, target_obj])
353 
354 #
355 # create_special_commands
356 #
357 # Return a string var that is all the source file commands for a particular file guy
358 # where we are going to execute a special command file.
359 #
360 def create_special_commands (a_file, target_obj, project):
361 
362  #
363  # Get the relative path to the file itself
364  #
365 
366  rel_file = srt_path_util.get_relative_path (a_file, project.get_project_file_dir())
367 
368  #
369  # Next, grab the name of the output object file name. Below we will assume
370  # that the output is an object, which may not always be the right thing
371  # to do!
372  #
373 
374  (basename, filename) = os.path.split (rel_file)
375  (file_name_root, file_ext) = os.path.splitext (filename)
376 
377  #
378  # Because DevStudio can only handle about 200 characters on their command
379  # lines, we have to write the commands to a file. We must make two of
380  # these files, one for the debug and release. Unfortunately, we have to
381  # assume that the same build commands go for each (no way around it). :(
382  #
383 
384  project_name = project.get_target_root_file()
385 
386  project_dir_debug_prefix = dir_debug_prefix + project_name
387  command_file_debug = project_dir_debug_prefix + "\\" + file_name_root + "_deb_b.bat"
388  project.create_ide_directory (project_dir_debug_prefix)
389 
390  project_dir_release_prefix = dir_release_prefix + project_name
391  command_file_release = project_dir_release_prefix + "\\" + file_name_root + "_rel_b.bat"
392  project.create_ide_directory (project_dir_release_prefix)
393 
394  #
395  # Write the commands out to the files
396  #
397 
398  cmd_file_d_handle = open (project.get_project_file_dir() + "\\" + command_file_debug, "w")
399  cmd_file_r_handle = open (project.get_project_file_dir() + "\\" + command_file_release, "w")
400 
401  source_commands = target_obj.get_special_commands()
402 
403  sub_dict = {}
404  sub_dict['_SOURCE_'] = rel_file
405  sub_dict['_OBJECT_'] = project_dir_release_prefix + "\\" + file_name_root + ".obj"
406  sub_dict['_TEMP_'] = project_dir_release_prefix + "\\"
407 
408  for a_command in source_commands:
409  cmd_file_r_handle.write(translate_command(a_command, sub_dict) + "\n")
410 
411  sub_dict['_OBJECT_'] = project_dir_debug_prefix + "\\" + file_name_root + ".obj"
412  sub_dict['_TEMP_'] = project_dir_debug_prefix + "\\"
413 
414  for a_command in source_commands:
415  cmd_file_d_handle.write(translate_command(a_command, sub_dict) + "\n")
416 
417  cmd_file_d_handle.close ()
418  cmd_file_r_handle.close ()
419 
420  #
421  # Now that we have the command files built, we need to insert the special
422  # build commands into the IDE dude.
423  #
424 
425  source_lines ='# Begin Source File\n\
426 \n\
427 SOURCE=' + rel_file + '\n\
428 \n\
429 !IF "$(CFG)" == "' + project.get_project_ide_name() + ' - Win32 Release"\n\
430 \n\
431 # PROP Ignore_Default_Tool 1 \n\
432 # Begin Custom Build - Fortran w/ Preprocessor\n\
433 InputPath=' + rel_file + '\n\
434 \n\
435 "' + project_dir_release_prefix + '\\' + file_name_root + '.obj" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"\n\
436  ' + command_file_release + '\n\
437 \n\
438 # End Custom Build\n\
439  \n\
440 !ELSEIF "$(CFG)" == "' + project.get_project_ide_name() + ' - Win32 Debug"\n\
441 \n\
442 # PROP Ignore_Default_Tool 1\n\
443 # Begin Custom Build - Fortran w/ Preprocessor\n\
444 InputPath=' + rel_file + '\n\
445 \n\
446 "' + project_dir_debug_prefix + '/' + file_name_root + '.obj" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"\n\
447  ' + command_file_debug + '\n\
448  \n\
449 # End Custom Build\n\
450 \n\
451 !ENDIF \n\
452 \n\
453 # End Source File\n'
454  return [source_lines, command_file_release, command_file_debug]
455 
456 
457 ###########################################
458 # translate_command
459 #
460 # Given a dictionary of replacements, do them in the current string
461 #
462 def translate_command (string, sub_dict):
463  result = string
464  for a_sub in sub_dict.keys():
465  result = regsub.gsub (a_sub, sub_dict[a_sub], result)
466  return result
467 
468 #############################################
469 #
470 # srt_ntide_compiler_include_file
471 #
472 # A helper object that will control writing out an inclue file
473 # for the compiler command line.
474 #
476  def __init__ (self, project, newrel_dir, name_modifier):
477  self.build_filename (project, newrel_dir, name_modifier)
478 
479  #
480  # build_filename -- build the damm filename out of the parts -- we want to
481  # store it in the project directory.
482  #
483  def build_filename (self, project, newrel_dir, name):
484  project_filename = project.get_project_file()
485  (project_file_base, extension) = os.path.splitext (project_filename)
486  self._filename = project_file_base + "_" + name + ".compiler_opt"
487 
488  #
489  # Open the file for output
490  #
491  def open (self):
492  return open (self._filename, "w")
493 
494  #
495  # name -- return the name of the file
496  #
497  def name (self):
498  return self._filename
499 
500 
501 #
502 # make_option_string
503 #
504 # Given a list of options, create a options string
505 #
506 def make_option_string (opt_list):
507  result = ""
508  for option in opt_list:
509  result = result + "/" + option + " "
510  return result
511 
512 #
513 # make_macro_string
514 #
515 # Do the same thing for macro guys
516 #
517 def make_macro_string (mac_list):
518  result = ""
519  for a_macro in mac_list:
520  split_pat=regex.compile("^\([^=]*\)=\(.*\)$")
521  if split_pat.match(a_macro) == -1:
522  result = result + '/D "' + a_macro + '" '
523  else:
524  result = result + '/D ' + split_pat.group(1) + '=' + split_pat.group(2) + " "
525  return result
526 
527 #
528 # make_include_string
529 #
530 # Returns a list (not a file) of include paths
531 #
532 def make_include_string (inc_list, proj_dir):
533  result = ""
534  for option in inc_list:
535  try:
536  rel_file = srt_path_util.get_relative_path (option, proj_dir)
537  except:
538  rel_file = option
539  result = result + "/I " + rel_file + " "
540  return result
def create_project_file(project, target_list, newrel_dir)
def translate_command(string, sub_dict)
translate_command
def __init__(self, project, newrel_dir, name_modifier)
def path_to_unix(the_path)
def get_file_class(filename)
def create_special_commands(a_file, target_obj, project)
def make_include_string(inc_list, proj_dir)