Mercurial > vim-crosoft
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. |