annotate scripts/vsutil.py @ 15:cfcac4ed7d21 default tip

Improve loading of solution files - New argument to force a rebuild of the cache - Gracefully handle missing projects in a solution - Handle more different xml namespaces - Support more edge cases
author Ludovic Chabant <ludovic@chabant.com>
date Tue, 29 Aug 2023 12:59:54 -0700
parents 4ba6df1b2f97
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
1 import copy
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
2 import logging
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
3 import os.path
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
4 import pickle
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
5 import re
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
6 import xml.etree.ElementTree as etree
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
7
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
8
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
9 # Known VS project types.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
10 PROJ_TYPE_FOLDER = '2150E333-8FDC-42A3-9474-1A3956D46DE8'
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
11 PROJ_TYPE_NMAKE = '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942'
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
12 PROJ_TYPE_CSHARP = 'FAE04EC0-301F-11D3-BF4B-00C04F79EFBC'
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
13
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
14 PROJ_TYPE_NAMES = {
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
15 PROJ_TYPE_FOLDER: 'folder',
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
16 PROJ_TYPE_NMAKE: 'nmake',
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
17 PROJ_TYPE_CSHARP: 'csharp'
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
18 }
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
19
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
20 # Known VS item types.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
21 ITEM_TYPE_CPP_SRC = 'ClCompile'
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
22 ITEM_TYPE_CPP_HDR = 'ClInclude'
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
23
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
24 ITEM_TYPE_CS_REF = 'Reference'
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
25 ITEM_TYPE_CS_PROJREF = 'ProjectReference'
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
26 ITEM_TYPE_CS_SRC = 'Compile'
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
27
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
28 ITEM_TYPE_NONE = 'None'
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
29
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
30 ITEM_TYPE_SOURCE_FILES = (ITEM_TYPE_CPP_SRC, ITEM_TYPE_CPP_HDR,
3
949c4f536f26 Add `None` file items to source solution files.
Ludovic Chabant <ludovic@chabant.com>
parents: 0
diff changeset
31 ITEM_TYPE_CS_SRC, ITEM_TYPE_NONE)
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
32
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
33 # Known VS properties.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
34 PROP_CONFIGURATION_TYPE = 'ConfigurationType'
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
35 PROP_NMAKE_PREPROCESSOR_DEFINITIONS = 'NMakePreprocessorDefinitions'
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
36 PROP_NMAKE_INCLUDE_SEARCH_PATH = 'NMakeIncludeSearchPath'
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
37
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
38
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
39 logger = logging.getLogger(__name__)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
40
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
41
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
42 def _strip_ns(tag):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
43 """ Remove the XML namespace from a tag name. """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
44 if tag[0] == '{':
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
45 i = tag.index('}')
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
46 return tag[i+1:]
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
47 return tag
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
48
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
49
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
50 re_msbuild_var = re.compile(r'\$\((?P<var>[\w\d_]+)\)')
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
51
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
52
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
53 def _resolve_value(val, env):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
54 """ Expands MSBuild property values given a build environment. """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
55 def _repl_vars(m):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
56 varname = m.group('var')
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
57 varval = env.get(varname, '')
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
58 return varval
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
59
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
60 if not val:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
61 return val
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
62 return re_msbuild_var.sub(_repl_vars, val)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
63
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
64
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
65 def _evaluate_condition(cond, env):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
66 """ Expands MSBuild property values in a condition and evaluates it. """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
67 left, right = _resolve_value(cond, env).split('==')
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
68 return left == right
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
69
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
70
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
71 class VSBaseGroup:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
72 """ Base class for VS project stuff that has conditional stuff inside.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
73
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
74 For instance, a property group called 'Blah' might have some common
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
75 (always valid) stuff, but a bunch of other stuff that should only
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
76 be considered when the solution configuration is Debug, Release, or
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
77 whatever else. In that case, each 'conditional' (i.e. values for Debug,
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
78 values for Release, etc.) is listed and tracked separately until
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
79 we are asked to 'resolve' ourselves based on a given build environment.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
80 """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
81 def __init__(self, label):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
82 self.label = label
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
83 self.conditionals = {}
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
84
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
85 def get_conditional(self, condition):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
86 """ Adds a conditional sub-group. """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
87 return self.conditionals.get(condition)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
88
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
89 def get_or_create_conditional(self, condition):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
90 """ Gets or creates a new conditional sub-group. """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
91 c = self.get_conditional(condition)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
92 if not c:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
93 c = self.__class__(self.label)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
94 self.conditionals[condition] = c
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
95 return c
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
96
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
97 def _resolve(self, env):
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
98 """ Resolves this group by evaluating each conditional sub-group
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
99 based on the given build environment. Returns a 'flattened'
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
100 version of ourselves.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
101 """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
102 c = self.__class__(self.label)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
103 c._collapse_child(self, env)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
104
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
105 for cond, child in self.conditionals.items():
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
106 if _evaluate_condition(cond, env):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
107 c._collapse_child(child, env)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
108
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
109 return c
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
110
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
111
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
112 class VSProjectItem:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
113 """ A VS project item, like a source code file. """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
114 def __init__(self, include, itemtype=None):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
115 self.include = include
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
116 self.itemtype = itemtype
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
117 self.metadata = {}
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
118
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
119 def _resolve(self, env):
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
120 c = VSProjectItem(_resolve_value(self.include, env), self.itemtype)
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
121 c.metadata = {k: _resolve_value(v, env)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
122 for k, v in self.metadata.items()}
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
123 return c
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
124
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
125 def __str__(self):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
126 return "(%s)%s" % (self.itemtype, self.include)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
127
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
128
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
129 class VSProjectItemGroup(VSBaseGroup):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
130 """ A VS project item group, like a list of source code files,
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
131 or a list of resources.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
132 """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
133 def __init__(self, label):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
134 super().__init__(label)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
135 self.items = []
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
136
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
137 def get_source_items(self):
3
949c4f536f26 Add `None` file items to source solution files.
Ludovic Chabant <ludovic@chabant.com>
parents: 0
diff changeset
138 return self.get_items_of_types(ITEM_TYPE_SOURCE_FILES)
949c4f536f26 Add `None` file items to source solution files.
Ludovic Chabant <ludovic@chabant.com>
parents: 0
diff changeset
139
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
140 def get_items_of_type(self, itemtype):
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
141 for i in self.items:
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
142 if i.itemtype == itemtype:
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
143 yield i
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
144
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
145 def get_items_of_types(self, itemtypes):
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
146 typeset = set(itemtypes)
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
147 for i in self.items:
3
949c4f536f26 Add `None` file items to source solution files.
Ludovic Chabant <ludovic@chabant.com>
parents: 0
diff changeset
148 if i.itemtype in typeset:
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
149 yield i
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
150
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
151 def _collapse_child(self, child, env):
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
152 self.items += [i._resolve(env) for i in child.items]
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
153
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
154
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
155 class VSProjectProperty:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
156 """ A VS project property, like an include path or compiler flag. """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
157 def __init__(self, name, value):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
158 self.name = name
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
159 self.value = value
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
160
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
161 def _resolve(self, env):
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
162 c = VSProjectProperty(self.name, _resolve_value(self.value, env))
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
163 return c
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
164
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
165 def __str__(self):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
166 return "%s=%s" % (self.name, self.value)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
167
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
168
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
169 class VSProjectPropertyGroup(VSBaseGroup):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
170 """ A VS project property group, such as compiler macros or flags. """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
171 def __init__(self, label):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
172 super().__init__(label)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
173 self.properties = []
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
174
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
175 def get(self, propname):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
176 try:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
177 return self[propname]
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
178 except IndexError:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
179 return None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
180
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
181 def __getitem__(self, propname):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
182 for p in self.properties:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
183 if p.name == propname:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
184 return p.value
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
185 raise IndexError()
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
186
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
187 def _collapse_child(self, child, env):
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
188 self.properties += [p._resolve(env) for p in child.properties]
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
189
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
190
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
191 class VSProject:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
192 """ A VS project. """
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
193 def __init__(self, owner, projtype, name, path, guid):
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
194 self.owner = owner
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
195 self.type = projtype
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
196 self.name = name
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
197 self.path = path
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
198 self.guid = guid
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
199 self._itemgroups = None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
200 self._propgroups = None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
201 self._sln = None
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
202 self._missing = False
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
203
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
204 @property
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
205 def is_folder(self):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
206 """ Returns whether this project is actually just a solution
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
207 folder, used as a container for other projects.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
208 """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
209 return self.type == PROJ_TYPE_FOLDER
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
210
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
211 @property
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
212 def abspath(self):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
213 abspath = self.path
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
214 if self._sln and self._sln.path:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
215 abspath = os.path.join(self._sln.dirpath, self.path)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
216 return abspath
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
217
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
218 @property
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
219 def absdirpath(self):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
220 return os.path.dirname(self.abspath)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
221
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
222 @property
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
223 def itemgroups(self):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
224 self._ensure_loaded()
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
225 return self._itemgroups.values()
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
226
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
227 @property
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
228 def propertygroups(self):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
229 self._ensure_loaded()
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
230 return self._propgroups.values()
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
231
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
232 def itemgroup(self, label, resolved_with=None):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
233 self._ensure_loaded()
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
234 ig = self._itemgroups.get(label)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
235 if resolved_with is not None and ig is not None:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
236 logger.debug("Resolving item group '%s'." % ig.label)
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
237 self._validate_build_env(resolved_with)
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
238 ig = ig._resolve(resolved_with)
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
239 return ig
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
240
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
241 def defaultitemgroup(self, resolved_with=None):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
242 return self.itemgroup(None, resolved_with=resolved_with)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
243
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
244 def propertygroup(self, label, resolved_with=None):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
245 self._ensure_loaded()
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
246 pg = self._propgroups.get(label)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
247 if resolved_with is not None and pg is not None:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
248 logger.debug("Resolving property group '%s'." % pg.label)
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
249 self._validate_build_env(resolved_with)
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
250 pg = pg._resolve(resolved_with)
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
251 return pg
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
252
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
253 def defaultpropertygroup(self, resolved_with=None):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
254 return self.propertygroup(None, resolved_with=resolved_with)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
255
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
256 def get_abs_item_include(self, item):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
257 return os.path.abspath(os.path.join(self.absdirpath, item.include))
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
258
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
259 def resolve(self, env):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
260 self._ensure_loaded()
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
261
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
262 self._validate_build_env(env)
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
263
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
264 propgroups = list(self._propgroups)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
265 itemgroups = list(self._itemgroups)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
266 self._propgroups[:] = []
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
267 self._itemgroups[:] = []
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
268
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
269 for pg in propgroups:
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
270 rpg = pg._resolve(env)
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
271 self._propgroups.append(rpg)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
272
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
273 for ig in itemgroups:
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
274 rig = ig._resolve(env)
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
275 self._itemgroups.append(rig)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
276
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
277 def _validate_build_env(self, buildenv):
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
278 buildenv['SolutionDir'] = self.owner.dirpath + os.path.sep
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
279 buildenv['ProjectDir'] = self.absdirpath + os.path.sep
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
280
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
281 def _ensure_loaded(self):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
282 if self._itemgroups is None or self._propgroups is None:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
283 self._load()
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
284
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
285 def _load(self):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
286 if not self.path:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
287 raise Exception("The current project has no path.")
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
288 if self.is_folder:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
289 logger.debug(f"Skipping folder project {self.name}")
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
290 self._itemgroups = {}
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
291 self._propgroups = {}
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
292 return
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
293
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
294 ns = {'ms': 'http://schemas.microsoft.com/developer/msbuild/2003'}
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
295
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
296 abspath = self.abspath
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
297 logger.debug(f"Loading project {self.name} ({self.path}) from: {abspath}")
4
ae0fb567f459 Report error but don't crash when a solution points to a missing project.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
298 try:
ae0fb567f459 Report error but don't crash when a solution points to a missing project.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
299 tree = etree.parse(abspath)
ae0fb567f459 Report error but don't crash when a solution points to a missing project.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
300 except (FileNotFoundError, OSError) as ex:
ae0fb567f459 Report error but don't crash when a solution points to a missing project.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
301 logger.debug(f"Error loading project {self.name}: " + str(ex))
ae0fb567f459 Report error but don't crash when a solution points to a missing project.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
302 self._itemgroups = {}
ae0fb567f459 Report error but don't crash when a solution points to a missing project.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
303 self._propgroups= {}
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
304 self._missing = True
4
ae0fb567f459 Report error but don't crash when a solution points to a missing project.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
305 return
ae0fb567f459 Report error but don't crash when a solution points to a missing project.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
306
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
307 root = tree.getroot()
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
308 if _strip_ns(root.tag) != 'Project':
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
309 raise Exception(f"Expected root node 'Project', got '{root.tag}'")
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
310
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
311 # Load ItemGroups and PropertyGroups via both namespaced names and raw
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
312 # names because not all types of VS projects use the MS namespaces.
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
313 self._itemgroups = {}
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
314 for itemgroupnode in root.iterfind('ItemGroup', ns):
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
315 self._load_item_group(itemgroupnode)
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
316 for itemgroupnode in root.iterfind('ms:ItemGroup', ns):
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
317 self._load_item_group(itemgroupnode)
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
318
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
319 self._propgroups = {}
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
320 for propgroupnode in root.iterfind('PropertyGroup', ns):
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
321 self._load_property_group(propgroupnode)
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
322 for propgroupnode in root.iterfind('ms:PropertyGroup', ns):
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
323 self._load_property_group(propgroupnode)
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
324
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
325 def _load_item_group(self, itemgroupnode):
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
326 label = itemgroupnode.attrib.get('Label')
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
327 itemgroup = self._itemgroups.get(label)
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
328 if not itemgroup:
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
329 itemgroup = VSProjectItemGroup(label)
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
330 self._itemgroups[label] = itemgroup
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
331 logger.debug(f"Adding itemgroup '{label}'")
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
332
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
333 condition = itemgroupnode.attrib.get('Condition')
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
334 if condition:
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
335 itemgroup = itemgroup.get_or_create_conditional(condition)
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
336
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
337 for itemnode in itemgroupnode:
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
338 incval = itemnode.attrib.get('Include')
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
339 item = VSProjectItem(incval, _strip_ns(itemnode.tag))
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
340 itemgroup.items.append(item)
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
341 for metanode in itemnode:
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
342 item.metadata[_strip_ns(metanode.tag)] = metanode.text
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
343
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
344 def _load_property_group(self, propgroupnode):
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
345 label = propgroupnode.attrib.get('Label')
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
346 propgroup = self._propgroups.get(label)
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
347 if not propgroup:
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
348 propgroup = VSProjectPropertyGroup(label)
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
349 self._propgroups[label] = propgroup
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
350 logger.debug(f"Adding propertygroup '{label}'")
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
351
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
352 condition = propgroupnode.attrib.get('Condition')
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
353 if condition:
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
354 propgroup = propgroup.get_or_create_conditional(condition)
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
355
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
356 for propnode in propgroupnode:
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
357 propgroup.properties.append(VSProjectProperty(
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
358 _strip_ns(propnode.tag),
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
359 propnode.text))
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
360
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
361
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
362 class MissingVSProjectError(Exception):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
363 pass
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
364
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
365
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
366 class VSGlobalSectionEntry:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
367 """ An entry in a VS solution's global section. """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
368 def __init__(self, name, value):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
369 self.name = name
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
370 self.value = value
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
371
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
372
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
373 class VSGlobalSection:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
374 """ A global section in a VS solution. """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
375 def __init__(self, name):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
376 self.name = name
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
377 self.entries = []
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
378
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
379
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
380 class VSSolution:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
381 """ A VS solution. """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
382 def __init__(self, path=None):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
383 self.path = path
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
384 self.projects = []
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
385 self.sections = []
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
386
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
387 @property
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
388 def dirpath(self):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
389 return os.path.dirname(self.path)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
390
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
391 def find_project_by_name(self, name, missing_ok=True):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
392 for p in self.projects:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
393 if p.name == name:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
394 return p
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
395 if missing_ok:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
396 return None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
397 return MissingVSProjectError(f"Can't find project with name: {name}")
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
398
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
399 def find_project_by_path(self, path, missing_ok=True):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
400 for p in self.projects:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
401 if p.abspath == path:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
402 return p
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
403 if missing_ok:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
404 return None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
405 raise MissingVSProjectError(f"Can't find project with path: {path}")
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
406
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
407 def find_project_by_guid(self, guid, missing_ok=True):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
408 for p in self.projects:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
409 if p.guid == guid:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
410 return p
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
411 if missing_ok:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
412 return None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
413 raise MissingVSProjectError(f"Can't find project for guid: {guid}")
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
414
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
415 def globalsection(self, name):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
416 for sec in self.sections:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
417 if sec.name == name:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
418 return sec
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
419 return None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
420
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
421 def find_project_configuration(self, proj_guid, sln_config):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
422 configs = self.globalsection('ProjectConfigurationPlatforms')
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
423 if not configs:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
424 return None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
425
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
426 entry_name = '{%s}.%s.Build.0' % (proj_guid, sln_config)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
427 for entry in configs.entries:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
428 if entry.name == entry_name:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
429 return entry.value
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
430 return None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
431
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
432
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
433 _re_sln_project_decl_start = re.compile(
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
434 r'^Project\("\{(?P<type>[A-Z0-9\-]+)\}"\) \= '
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
435 r'"(?P<name>[^"]+)", "(?P<path>[^"]+)", "\{(?P<guid>[A-Z0-9\-]+)\}"$')
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
436 _re_sln_project_decl_end = re.compile(
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
437 r'^EndProject$')
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
438
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
439 _re_sln_global_start = re.compile(r'^Global$')
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
440 _re_sln_global_end = re.compile(r'^EndGlobal$')
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
441 _re_sln_global_section_start = re.compile(
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
442 r'^\s*GlobalSection\((?P<name>\w+)\) \= (?P<step>\w+)$')
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
443 _re_sln_global_section_end = re.compile(r'^\s*EndGlobalSection$')
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
444
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
445
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
446 def parse_sln_file(slnpath):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
447 """ Parses a solution file, returns a solution object.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
448 The projects are not loaded (they will be lazily loaded upon
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
449 first access to their items/properties/etc.).
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
450 """
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
451 logging.debug(f"Reading {slnpath}")
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
452 slnobj = VSSolution(slnpath)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
453 with open(slnpath, 'r') as fp:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
454 lines = fp.readlines()
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
455 _parse_sln_file_text(slnobj, lines)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
456 return slnobj
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
457
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
458
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
459 def _parse_sln_file_text(slnobj, lines):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
460 until = None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
461 in_global = False
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
462 in_global_section = None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
463
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
464 for i, line in enumerate(lines):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
465 if until:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
466 # We need to parse something until a given token, so let's
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
467 # do that and ignore everything else.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
468 m = until.search(line)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
469 if m:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
470 until = None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
471 continue
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
472
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
473 if in_global:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
474 # We're in the 'global' part of the solution. It should contain
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
475 # a bunch of 'global sections' that we need to parse individually.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
476 if in_global_section:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
477 # Keep parsing the current section until we reach the end.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
478 m = _re_sln_global_section_end.search(line)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
479 if m:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
480 in_global_section = None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
481 continue
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
482
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
483 ename, evalue = line.strip().split('=')
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
484 in_global_section.entries.append(VSGlobalSectionEntry(
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
485 ename.strip(),
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
486 evalue.strip()))
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
487 continue
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
488
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
489 m = _re_sln_global_section_start.search(line)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
490 if m:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
491 # Found the start of a new section.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
492 in_global_section = VSGlobalSection(m.group('name'))
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
493 logging.debug(f" Adding global section {in_global_section.name} (line {i})")
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
494 slnobj.sections.append(in_global_section)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
495 continue
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
496
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
497 m = _re_sln_global_end.search(line)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
498 if m:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
499 # Found the end of the 'global' part.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
500 in_global = False
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
501 continue
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
502
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
503 # We're not in a specific part of the solution, so do high-level
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
504 # parsing. First, ignore root-level comments.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
505 if not line or line[0] == '#':
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
506 continue
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
507
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
508 m = _re_sln_project_decl_start.search(line)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
509 if m:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
510 # Found the start of a project declaration.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
511 try:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
512 p = VSProject(
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
513 slnobj,
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
514 m.group('type'), m.group('name'), m.group('path'),
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
515 m.group('guid'))
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
516 except:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
517 raise Exception(f"Error line {i}: unexpected project syntax.")
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
518 logging.debug(f" Adding project {p.name} (line {i})")
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
519 slnobj.projects.append(p)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
520 p._sln = slnobj
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
521
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
522 until = _re_sln_project_decl_end
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
523 continue
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
524
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
525 m = _re_sln_global_start.search(line)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
526 if m:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
527 # Reached the start of the 'global' part, where global sections
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
528 # are defined.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
529 in_global = True
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
530 continue
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
531
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
532 # Ignore the rest (like visual studio version flags).
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
533 continue
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
534
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
535
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
536 class SolutionCache:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
537 """ A class that contains a VS solution object, along with pre-indexed
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
538 lists of items. It's meant to be saved on disk.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
539 """
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
540 VERSION = 5
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
541
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
542 def __init__(self, slnobj):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
543 self.slnobj = slnobj
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
544 self.index = None
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
545 self._saved_version = SolutionCache.VERSION
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
546
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
547 def build_cache(self):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
548 self.index = {}
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
549 for proj in self.slnobj.projects:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
550 if proj.is_folder:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
551 continue
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
552 itemgroup = proj.defaultitemgroup()
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
553 if not itemgroup:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
554 continue
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
555
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
556 item_cache = set()
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
557 self.index[proj.abspath] = item_cache
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
558
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
559 for item in itemgroup.get_source_items():
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
560 if item.include:
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
561 item_path = proj.get_abs_item_include(item).lower()
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
562 item_cache.add(item_path)
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
563 # else: it's an item from our shortlist (cpp, cs, etc files)
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
564 # but it somehow doesn't have a path, which can happen with
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
565 # some obscure VS features.
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
566
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
567 def save(self, path):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
568 pathdir = os.path.dirname(path)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
569 if not os.path.exists(pathdir):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
570 os.makedirs(pathdir)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
571 with open(path, 'wb') as fp:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
572 pickle.dump(self, fp)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
573
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
574 @staticmethod
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
575 def load_or_rebuild(slnpath, cachepath, force_rebuild=False):
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
576 if cachepath and not force_rebuild:
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
577 res = _try_load_from_cache(slnpath, cachepath)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
578 if res is not None:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
579 return res
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
580
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
581 slnobj = parse_sln_file(slnpath)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
582 cache = SolutionCache(slnobj)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
583
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
584 if cachepath:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
585 logger.debug(f"Regenerating cache: {cachepath}")
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
586 cache.build_cache()
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
587 cache.save(cachepath)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
588
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
589 return (cache, False)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
590
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
591
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
592 def _try_load_from_cache(slnpath, cachepath):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
593 try:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
594 sln_dt = os.path.getmtime(slnpath)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
595 cache_dt = os.path.getmtime(cachepath)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
596 except OSError:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
597 logger.debug("Can't read solution or cache files.")
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
598 return None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
599
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
600 # If the solution file is newer, bail out.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
601 if sln_dt >= cache_dt:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
602 logger.debug("Solution is newer than cache.")
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
603 return None
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
604
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
605 # Our cache is at least valid for the solution stuff. Some of our
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
606 # projects might be out of date, but at least there can't be any
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
607 # added or removed projects from the solution (otherwise the solution
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
608 # file would have been touched). Let's load the cache.
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
609 try:
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
610 with open(cachepath, 'rb') as fp:
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
611 cache = pickle.load(fp)
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
612 except Exception as ex:
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
613 logger.debug("Error loading solution cache: %s" % ex)
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
614 logger.debug("Deleting cache: %s" % cachepath)
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
615 os.remove(cachepath)
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
616 return None
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
617
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
618 # Check that the cache version is up-to-date with this code.
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
619 loaded_ver = getattr(cache, '_saved_version', 0)
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
620 if loaded_ver != SolutionCache.VERSION:
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
621 logger.debug(f"Cache was saved with older format: {cachepath} "
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
622 f"(got {loaded_ver}, expected {SolutionCache.VERSION})")
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
623 return None
9
4ba6df1b2f97 Add built-in properties to the solution environment before resolving items.
Ludovic Chabant <ludovic@chabant.com>
parents: 4
diff changeset
624 logger.debug(f"Cache has correct version: {loaded_ver}")
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
625
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
626 slnobj = cache.slnobj
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
627
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
628 # Check that none of the project files in the solution are newer
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
629 # than this cache.
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
630 proj_dts = []
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
631 for p in slnobj.projects:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
632 if not p.is_folder:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
633 try:
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
634 proj_dts.append(os.path.getmtime(p.abspath))
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
635 # The project was missing last time we built the cache,
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
636 # but now it exists. Force a rebuild.
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
637 if p._missing:
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
638 return None
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
639 except OSError:
15
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
640 if not p._missing:
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
641 logger.debug(f"Found missing project: {p.abspath}")
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
642 return None
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
643 # else: it was already missing last time we built the
cfcac4ed7d21 Improve loading of solution files
Ludovic Chabant <ludovic@chabant.com>
parents: 9
diff changeset
644 # cache, so nothing has changed.
0
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
645
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
646 if all([cache_dt > pdt for pdt in proj_dts]):
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
647 logger.debug(f"Cache is up to date: {cachepath}")
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
648 return (cache, True)
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
649
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
650 logger.debug("Cache has outdated projects.")
5d2c0db51914 Initial commit
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
651 return None