comparison scripts/vsutil.py @ 9:4ba6df1b2f97

Add built-in properties to the solution environment before resolving items.
author Ludovic Chabant <ludovic@chabant.com>
date Thu, 24 Sep 2020 22:57:50 -0700
parents ae0fb567f459
children cfcac4ed7d21
comparison
equal deleted inserted replaced
8:d5ddd9ffaf11 9:4ba6df1b2f97
27 27
28 ITEM_TYPE_NONE = 'None' 28 ITEM_TYPE_NONE = 'None'
29 29
30 ITEM_TYPE_SOURCE_FILES = (ITEM_TYPE_CPP_SRC, ITEM_TYPE_CPP_HDR, 30 ITEM_TYPE_SOURCE_FILES = (ITEM_TYPE_CPP_SRC, ITEM_TYPE_CPP_HDR,
31 ITEM_TYPE_CS_SRC, ITEM_TYPE_NONE) 31 ITEM_TYPE_CS_SRC, ITEM_TYPE_NONE)
32
33 32
34 # Known VS properties. 33 # Known VS properties.
35 PROP_CONFIGURATION_TYPE = 'ConfigurationType' 34 PROP_CONFIGURATION_TYPE = 'ConfigurationType'
36 PROP_NMAKE_PREPROCESSOR_DEFINITIONS = 'NMakePreprocessorDefinitions' 35 PROP_NMAKE_PREPROCESSOR_DEFINITIONS = 'NMakePreprocessorDefinitions'
37 PROP_NMAKE_INCLUDE_SEARCH_PATH = 'NMakeIncludeSearchPath' 36 PROP_NMAKE_INCLUDE_SEARCH_PATH = 'NMakeIncludeSearchPath'
93 if not c: 92 if not c:
94 c = self.__class__(self.label) 93 c = self.__class__(self.label)
95 self.conditionals[condition] = c 94 self.conditionals[condition] = c
96 return c 95 return c
97 96
98 def resolve(self, env): 97 def _resolve(self, env):
99 """ Resolves this group by evaluating each conditional sub-group 98 """ Resolves this group by evaluating each conditional sub-group
100 based on the given build environment. Returns a 'flattened' 99 based on the given build environment. Returns a 'flattened'
101 version of ourselves. 100 version of ourselves.
102 """ 101 """
103 c = self.__class__(self.label) 102 c = self.__class__(self.label)
115 def __init__(self, include, itemtype=None): 114 def __init__(self, include, itemtype=None):
116 self.include = include 115 self.include = include
117 self.itemtype = itemtype 116 self.itemtype = itemtype
118 self.metadata = {} 117 self.metadata = {}
119 118
120 def resolve(self, env): 119 def _resolve(self, env):
121 c = VSProjectItem(_resolve_value(self.include), self.itemtype) 120 c = VSProjectItem(_resolve_value(self.include, env), self.itemtype)
122 c.metadata = {k: _resolve_value(v, env) 121 c.metadata = {k: _resolve_value(v, env)
123 for k, v in self.metadata.items()} 122 for k, v in self.metadata.items()}
124 return c 123 return c
125 124
126 def __str__(self): 125 def __str__(self):
136 self.items = [] 135 self.items = []
137 136
138 def get_source_items(self): 137 def get_source_items(self):
139 return self.get_items_of_types(ITEM_TYPE_SOURCE_FILES) 138 return self.get_items_of_types(ITEM_TYPE_SOURCE_FILES)
140 139
141 def get_items_of_types(self, *itemtypes): 140 def get_items_of_type(self, itemtype):
142 typeset = set(*itemtypes) 141 for i in self.items:
142 if i.itemtype == itemtype:
143 yield i
144
145 def get_items_of_types(self, itemtypes):
146 typeset = set(itemtypes)
143 for i in self.items: 147 for i in self.items:
144 if i.itemtype in typeset: 148 if i.itemtype in typeset:
145 yield i 149 yield i
146 150
147 def _collapse_child(self, child, env): 151 def _collapse_child(self, child, env):
148 self.items += [i.resolve(env) for i in child.items] 152 self.items += [i._resolve(env) for i in child.items]
149 153
150 154
151 class VSProjectProperty: 155 class VSProjectProperty:
152 """ A VS project property, like an include path or compiler flag. """ 156 """ A VS project property, like an include path or compiler flag. """
153 def __init__(self, name, value): 157 def __init__(self, name, value):
154 self.name = name 158 self.name = name
155 self.value = value 159 self.value = value
156 160
157 def resolve(self, env): 161 def _resolve(self, env):
158 c = VSProjectProperty(self.name, _resolve_value(self.value, env)) 162 c = VSProjectProperty(self.name, _resolve_value(self.value, env))
159 return c 163 return c
160 164
161 def __str__(self): 165 def __str__(self):
162 return "%s=%s" % (self.name, self.value) 166 return "%s=%s" % (self.name, self.value)
179 if p.name == propname: 183 if p.name == propname:
180 return p.value 184 return p.value
181 raise IndexError() 185 raise IndexError()
182 186
183 def _collapse_child(self, child, env): 187 def _collapse_child(self, child, env):
184 self.properties += [p.resolve(env) for p in child.properties] 188 self.properties += [p._resolve(env) for p in child.properties]
185 189
186 190
187 class VSProject: 191 class VSProject:
188 """ A VS project. """ 192 """ A VS project. """
189 def __init__(self, projtype, name, path, guid): 193 def __init__(self, owner, projtype, name, path, guid):
194 self.owner = owner
190 self.type = projtype 195 self.type = projtype
191 self.name = name 196 self.name = name
192 self.path = path 197 self.path = path
193 self.guid = guid 198 self.guid = guid
194 self._itemgroups = None 199 self._itemgroups = None
226 def itemgroup(self, label, resolved_with=None): 231 def itemgroup(self, label, resolved_with=None):
227 self._ensure_loaded() 232 self._ensure_loaded()
228 ig = self._itemgroups.get(label) 233 ig = self._itemgroups.get(label)
229 if resolved_with is not None and ig is not None: 234 if resolved_with is not None and ig is not None:
230 logger.debug("Resolving item group '%s'." % ig.label) 235 logger.debug("Resolving item group '%s'." % ig.label)
231 ig = ig.resolve(resolved_with) 236 self._validate_build_env(resolved_with)
237 ig = ig._resolve(resolved_with)
232 return ig 238 return ig
233 239
234 def defaultitemgroup(self, resolved_with=None): 240 def defaultitemgroup(self, resolved_with=None):
235 return self.itemgroup(None, resolved_with=resolved_with) 241 return self.itemgroup(None, resolved_with=resolved_with)
236 242
237 def propertygroup(self, label, resolved_with=None): 243 def propertygroup(self, label, resolved_with=None):
238 self._ensure_loaded() 244 self._ensure_loaded()
239 pg = self._propgroups.get(label) 245 pg = self._propgroups.get(label)
240 if resolved_with is not None and pg is not None: 246 if resolved_with is not None and pg is not None:
241 logger.debug("Resolving property group '%s'." % pg.label) 247 logger.debug("Resolving property group '%s'." % pg.label)
242 pg = pg.resolve(resolved_with) 248 self._validate_build_env(resolved_with)
249 pg = pg._resolve(resolved_with)
243 return pg 250 return pg
244 251
245 def defaultpropertygroup(self, resolved_with=None): 252 def defaultpropertygroup(self, resolved_with=None):
246 return self.propertygroup(None, resolved_with=resolved_with) 253 return self.propertygroup(None, resolved_with=resolved_with)
247 254
248 def get_abs_item_include(self, item): 255 def get_abs_item_include(self, item):
249 return os.path.abspath(os.path.join(self.absdirpath, item.include)) 256 return os.path.abspath(os.path.join(self.absdirpath, item.include))
250 257
251 def resolve(self, env): 258 def resolve(self, env):
252 self._ensure_loaded() 259 self._ensure_loaded()
260
261 self._validate_build_env(env)
253 262
254 propgroups = list(self._propgroups) 263 propgroups = list(self._propgroups)
255 itemgroups = list(self._itemgroups) 264 itemgroups = list(self._itemgroups)
256 self._propgroups[:] = [] 265 self._propgroups[:] = []
257 self._itemgroups[:] = [] 266 self._itemgroups[:] = []
258 267
259 for pg in propgroups: 268 for pg in propgroups:
260 rpg = pg.resolve(env) 269 rpg = pg._resolve(env)
261 self._propgroups.append(rpg) 270 self._propgroups.append(rpg)
262 271
263 for ig in itemgroups: 272 for ig in itemgroups:
264 rig = ig.resolve(env) 273 rig = ig._resolve(env)
265 self._itemgroups.append(rig) 274 self._itemgroups.append(rig)
275
276 def _validate_build_env(self, buildenv):
277 buildenv['SolutionDir'] = self.owner.dirpath + os.path.sep
278 buildenv['ProjectDir'] = self.absdirpath + os.path.sep
266 279
267 def _ensure_loaded(self): 280 def _ensure_loaded(self):
268 if self._itemgroups is None or self._propgroups is None: 281 if self._itemgroups is None or self._propgroups is None:
269 self._load() 282 self._load()
270 283
481 m = _re_sln_project_decl_start.search(line) 494 m = _re_sln_project_decl_start.search(line)
482 if m: 495 if m:
483 # Found the start of a project declaration. 496 # Found the start of a project declaration.
484 try: 497 try:
485 p = VSProject( 498 p = VSProject(
499 slnobj,
486 m.group('type'), m.group('name'), m.group('path'), 500 m.group('type'), m.group('name'), m.group('path'),
487 m.group('guid')) 501 m.group('guid'))
488 except: 502 except:
489 raise Exception(f"Error line {i}: unexpected project syntax.") 503 raise Exception(f"Error line {i}: unexpected project syntax.")
490 logging.debug(f" Adding project {p.name}") 504 logging.debug(f" Adding project {p.name}")
507 521
508 class SolutionCache: 522 class SolutionCache:
509 """ A class that contains a VS solution object, along with pre-indexed 523 """ A class that contains a VS solution object, along with pre-indexed
510 lists of items. It's meant to be saved on disk. 524 lists of items. It's meant to be saved on disk.
511 """ 525 """
512 VERSION = 3 526 VERSION = 4
513 527
514 def __init__(self, slnobj): 528 def __init__(self, slnobj):
515 self.slnobj = slnobj 529 self.slnobj = slnobj
516 self.index = None 530 self.index = None
531 self._saved_version = SolutionCache.VERSION
517 532
518 def build_cache(self): 533 def build_cache(self):
519 self.index = {} 534 self.index = {}
520 for proj in self.slnobj.projects: 535 for proj in self.slnobj.projects:
521 if proj.is_folder: 536 if proj.is_folder:
575 # file would have been touched). Let's load the cache. 590 # file would have been touched). Let's load the cache.
576 with open(cachepath, 'rb') as fp: 591 with open(cachepath, 'rb') as fp:
577 cache = pickle.load(fp) 592 cache = pickle.load(fp)
578 593
579 # Check that the cache version is up-to-date with this code. 594 # Check that the cache version is up-to-date with this code.
580 loaded_ver = getattr(cache, 'VERSION') 595 loaded_ver = getattr(cache, '_saved_version', 0)
581 if loaded_ver != SolutionCache.VERSION: 596 if loaded_ver != SolutionCache.VERSION:
582 logger.debug(f"Cache was saved with older format: {cachepath}") 597 logger.debug(f"Cache was saved with older format: {cachepath} "
598 f"(got {loaded_ver}, expected {SolutionCache.VERSION})")
583 return None 599 return None
600 logger.debug(f"Cache has correct version: {loaded_ver}")
584 601
585 slnobj = cache.slnobj 602 slnobj = cache.slnobj
586 603
587 # Check that none of the project files in the solution are newer 604 # Check that none of the project files in the solution are newer
588 # than this cache. 605 # than this cache.