-
Notifications
You must be signed in to change notification settings - Fork 2
/
solution.py
175 lines (138 loc) · 5.12 KB
/
solution.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
"""
Visual Studio Solution Generator; With love by [email protected]
WARNING: a lot of hardcoded stuff, please forgive me, at least I documented it :D
NamingNotationConventions:
---
Every global _SLN var with _TEMPLATE suffix has some variable to replace for
example all occurrences of $NAME should be replaced with project name.
Global _SLN variables explained:
----
_SLN_HEADER - the thing that comes first
_SLN_PROJECT_TEMPLATE - declaration for each of the projects
$NAME - project name
$GUID - project GUID
_SLN_GLOBAL_HEADER - comes after the project declarations
_SLN_GLOBAL_PROJECT_CONFIG_TEMPLATE - config for each of the projects
$GUID - project GUID
_SLN_GLOBAL_END_TEMPLATE - the footer of the whole SLN file
$SOLUTION_GUID - GUID for this solution
"""
from typing import Iterable, Dict, Tuple, Union, Optional
import re
import os
import uuid
_SLN_HEADER = """\
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31702.278
MinimumVisualStudioVersion = 10.0.40219.1
"""
# The hardcoded GUID (8BC9CE...C942) is project type GUID (Magshimim_EX)
_SLN_PROJECT_TEMPLATE = """\
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "$NAME", "$NAME.vcxproj", "{$GUID}"
EndProject
"""
_SLN_GLOBAL_HEADER = """\
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
"""
_SLN_GLOBAL_PROJECT_CONFIG_TEMPLATE = """\
{$GUID}.Debug|x64.ActiveCfg = Debug|x64
{$GUID}.Debug|x64.Build.0 = Debug|x64
{$GUID}.Debug|x86.ActiveCfg = Debug|Win32
{$GUID}.Debug|x86.Build.0 = Debug|Win32
{$GUID}.Release|x64.ActiveCfg = Release|x64
{$GUID}.Release|x64.Build.0 = Release|x64
{$GUID}.Release|x86.ActiveCfg = Release|Win32
{$GUID}.Release|x86.Build.0 = Release|Win32
"""
_SLN_GLOBAL_END_TEMPLATE = """\
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {$SOLUTION_GUID}
EndGlobalSection
EndGlobal
"""
def filenames_to_uuids(filenames: Iterable[str]) -> Dict[str, uuid.UUID]:
"""
Generates a dictionary of filename:UUID from the given filenames.
@param filenames list of file names for which to generate the dictionary.
@return a dictionary of filename:UUID.
"""
return {filename: uuid.uuid4() for filename in filenames}
def load_sln(
solution_path: Union[str, os.PathLike]
) -> Optional[Tuple[str, uuid.UUID, uuid.UUID]]:
"""
Loads an existing VisualStudio sln file that contains one project in the
same dir.
@param solution_path Path to the sln file to load.
@return If the solution is valid, a tuple of
( the contents of the loaded sln file,
the existing project GUID,
the existing solution GUID)
Else None.
@see generate_sln
"""
with open(solution_path, "r") as f:
solution = f.read()
GUID_REGEX = r"\{[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}\}"
PROJECT_GUID_REGEX = re.compile(
r'Project\("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"\) = "[^"]+", "[^"]+", "('
+ GUID_REGEX
+ r')"'
)
SOLUTION_GUID_REGEX = re.compile(r"SolutionGuid = (" + GUID_REGEX + ")")
project_guid_match = PROJECT_GUID_REGEX.search(solution)
solution_guid_match = SOLUTION_GUID_REGEX.search(solution)
if project_guid_match is None or solution_guid_match is None:
return None
project_guid = uuid.UUID(project_guid_match.group(1))
solution_guid = uuid.UUID(solution_guid_match.group(1))
return solution, project_guid, solution_guid
def generate_sln(solution_name) -> Tuple[str, uuid.UUID, uuid.UUID]:
"""
Generates a VisualStudio sln file for a project in the same dir as the sln.
@param solution_name the name of the whole solution. This will be the name of the project.
@return the generated sln file as a string,
project GUID,
and solution GUID.
@todo maybe instead of .replace use something better than just text juggling.
"""
project_name = solution_name
project_guid = uuid.uuid4()
solution = _SLN_HEADER
solution_guid = uuid.uuid4()
solution += _SLN_PROJECT_TEMPLATE.replace("$NAME", project_name).replace(
"$GUID", str(project_guid)
)
solution += _SLN_GLOBAL_HEADER
solution += _SLN_GLOBAL_PROJECT_CONFIG_TEMPLATE.replace(
"$GUID", str(project_guid)
)
solution += _SLN_GLOBAL_END_TEMPLATE.replace(
"$SOLUTION_GUID", str(solution_guid)
)
return (solution, project_guid, solution_guid)
def main():
print(
"Example sln generation for the files q1.c q2.c q3.c and the functions upper() and replace('.c', ''):"
)
sln, projects, sln_guid = generate_sln(
filename.replace(".c", "").upper()
for filename in ["q1.c", "q2.c", "q3.c"]
)
print(f"{projects=}")
print(f"{sln_guid=}")
print(sln)
if __name__ == "__main__":
main()