-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathclmod.py
executable file
·234 lines (202 loc) · 6.95 KB
/
clmod.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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#! /usr/bin/env python
##############################################################################
# Python script to let Visual C++ deal with C++ source code using modules
# - supporting arbitrary file suffixes
# - without the need to use undocumented options
# - passing all files one one command line
#
# Can also be used as skeleton for other tools to handle C++ units differently.
#
# Special handling:
# Pass "-debug" to have verbose output
##############################################################################
import sys
import os
import re
import subprocess
##############################################################################
# global flags
##############################################################################
debug = False # may be turned on with -debug
##############################################################################
# getModulesType()
# - parses the give file to find out its unit type:
# - returns:
# "tu" for traditional translation unit
# "ifm" for primary module interface unit
# "ifp" for interface partition unit
# "int" for internal partition unit
# "impl" for module im0plementation unit
##############################################################################
def getModulesType(filename):
#print("\n=== getModulesOpt() for", filename)
if not os.path.isfile(filename):
print ("*** ERROR: '" + filename + "' doesn't exist")
sys.exit(1)
inGlobalModuleFragment = False
inComment = False
with open(filename, 'r') as f:
for rawline in f:
line = rawline.strip()
# am I inside /* ... */ comment?
if inComment:
idxClose = line.find("*/")
if idxClose >= 0:
line = line[idxClose+2:]
inComment = False
else:
continue
# ignore // comment
idx = line.find("//")
if idx >= 0:
line = line[:idx]
# ignore /* ... */ comment
idxOpen = line.find("/*")
while idxOpen >= 0:
idxClose = line.find("*/")
if idxClose >= 0:
line = line[:idxOpen] + line[idxClose+2:]
else:
line = line[:idxOpen]
inComment = True
break
idxOpen = line.find("/*")
# ignore empty line:
if not line:
continue
if line.startswith("module;"):
inGlobalModuleFragment = True
continue
# preprocessor commands in global module fragment ignored:
if inGlobalModuleFragment and line.startswith("#"):
continue
if not line.startswith("module") and \
not line.startswith("export module"):
# traditional translation unit => no special options
if debug:
print(" >", rawline)
print(" ", filename, "is no module unit")
return "tu" # RETURN "traditional translation unit"
# module unit: check its type:
m = re.search("export module ([a-zA-Z0-9]*):([a-zA-Z0-9]*)", line)
if m:
# interface partition:
if debug:
print(" >", rawline)
print(" ", filename, "is interface partition '" + m.group(2) +
"' in module '" + m.group(1) + "'")
return "ifp" # RETURN "interface partition"
m = re.search("export module ([a-zA-Z0-9]*)", line)
if m:
# primary module interface:
if debug:
print(" >", rawline)
print(" ", filename, "is interface of module '" + m.group(1) + "'")
return "ifm" # RETURN "primary module interface"
m = re.search("module ([a-zA-Z0-9]*):([a-zA-Z0-9]*)", line)
if m:
# internal partition:
if debug:
print(" >", rawline)
print(" ", filename, "is internal partition '" + m.group(2) +
"' in module '" + m.group(1) + "'")
return "int" # RETURN "internal partition"
m = re.search("module ([a-zA-Z0-9]*)", line)
if m:
# implementation unit:
if debug:
print(" >", rawline)
print(" ", filename, "is implementation unit of module '" + m.group(1) + "'")
return "impl" # RETURN "implementation unit
# could not decide:
print("*** ERROR: could not categorize", filename)
sys.exit(1)
##############################################################################
# getModulesOpt()
# - return the command line options Visual C++ need for the passed unit
##############################################################################
def getModulesOpt(filename):
type = getModulesType(filename)
if type == "int":
return "/internalPartition"
if type == "ifm":
return "/interface"
if type == "ifp":
return "/interface"
if type == "impl":
return ""
if type == "tu":
return ""
# could not decide:
print("*** ERROR: invalid unit type: ", type, "for", filename)
sys.exit(1)
##############################################################################
# run()
# - runs a systemn command with the passed args
##############################################################################
def run(args):
# since Python 3.4 we can use run() but let's support older Python versions:
# if sys.version_info[0] > 3 or \
# (sys.version_info[0] > 2 and sys.version_info[1] > 4):
# p = subprocess.run(args)
p = subprocess.Popen(args)
p.wait()
return p
##############################################################################
# MAIN
##############################################################################
# parse all command-line options
# - separationg files (having a dot inside)
# - from compiler/linker options (the rest)
files = []
flags = []
compileOnly = False
for opt in sys.argv[1:]:
#print(opt)
if (opt == "-debug"):
debug = True
continue
if (opt == "/c"):
compileOnly = False
continue
if opt.startswith("/"):
flags.append(opt)
continue
if "." in opt:
files.append(opt)
else:
flags.append(opt)
#print("files: ", files)
#print("flags: ", flags)
# iterate over all files
# - checking the unit type
# - traditional non-module translation unit or
# - one of the module unit types
# - compile each file accordingly
print("COMPILE:")
objfiles = []
for filename in files:
print("\n*** COMPILE '" + filename + "'")
m = re.search("^(.*)\.([^.]*)$", filename)
if not m:
print("*** ERROR: file", filename, "has no suffix")
sys.exit(1)
fileopt = getModulesOpt(filename)
# compile code:
print("*** cl ", " ".join(flags), "/TP", "/c", fileopt, filename)
if not fileopt or fileopt == "":
p = run(["cl"] + flags + ["/TP", "/c", filename])
else:
p = run(["cl"] + flags + ["/TP", "/c", fileopt, filename])
if p.returncode != 0:
print("*** ERROR: compiling", filename, "failed")
sys.exit(1)
objfiles.append(m.group(1) + ".obj")
# link generated object files
if not compileOnly:
print("\n*** LINK:")
print("*** cl ", " ".join(flags), " ".join(objfiles))
p = run(["cl"] + flags + objfiles)
if p.returncode != 0:
print("*** ERROR: linking failed")
sys.exit(1)