forked from rljacobson/MathLine
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconfigure.py
executable file
·335 lines (273 loc) · 11.3 KB
/
configure.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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
#! /usr/bin/env python
import shutil
import os
import sys
import subprocess
import platform
import argparse as ap
makefile_template = """# This makefile was autogenerated by configure.py.
CPP=%(cpp_compiler)s
CC=%(c_compiler)s
CFLAGS=%(cflags)s
LDFLAGS=%(lflags)s
CPPSOURCES=%(cpp_sources)s
CSOURCES=%(c_sources)s
OBJECTS=$(CPPSOURCES:.cpp=.o) $(CSOURCES:.c=.o)
EXECUTABLE=%(binary)s
all: $(CPPSOURCES) $(CSOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
\t$(CPP) $(LDFLAGS) $(OBJECTS) -o $@
.cpp.o:
\t$(CPP) $(CFLAGS) $< -o $@
.c.o:
\t$(CC) $(CFLAGS) $< -o $@
clean:
\t./configure.py -clean
install:
\tinstall -c $(EXECUTABLE) "%(prefix)s/bin"
"""
cpp_compiler = "g++" # Files with a .cpp extension are compiled as c++ files.
c_compiler = "gcc" # Files with a .c extension are compiled as c files.
tmp_path = "build" # We build everything in a build directory.
output_path = "build" # If empty, we use the tmp_path directory.
output_file = "MathLine"
source_files = [
"main.cpp",
"mlbridge.cpp",
"linenoise.c"
]
# Absolute path to Mathematica installation directory. We determine this
# directory by evaluating $InstallationDirectory in Mathematica.
mma_install_path = "" # "/Applications/Mathematica.app/Contents"
# Set-able via command-line argument --mma_path="/path/to/MathKernel"
mma_kernel = "math"
# The path to the wstp developer files, relative to mma_installation_path.
# According to: https://reference.wolfram.com/language/tutorial/WSTPDeveloperGuide-Unix.html
# "The WSTP Developer Kit (WSDK) is located in the directory
# $InstallationDirectory/SystemFiles/Links/WSTP/DeveloperKit/$SystemID within your Wolfram
# System directory."
wstp_path = "/SystemFiles/Links/WSTP/DeveloperKit"
mathlink_path = "/SystemFiles/Links/MathLink/DeveloperKit"
# mma_link_path either the wstp_path or the mathlink_path, depending on Mathematica version
mma_link_path = ""
# Are we using the WSTP library (True) or the MathLink library (False)?
wstp = True
# The path to wstp.h, relative to mma_installation_path.
include_path = "" # E.g. "/MacOSX-x86-64/CompilerAdditions"
# The path to libWSTPi4.a, relative to mma_installation_path.
library_path = "" # E.g. "/MacOSX-x86-64/CompilerAdditions/AlternativeLibraries"
# The libraries that the final binary should be linked with. These library
# names will be prefixed with the usual "-l" when passed to the linker.
link_libs = ["boost_program_options"]
# Any other flags to pass to the linker.
other_link_flags = []
# Any other flags to pass to the compilers. Why not -O2?
other_compiler_flags = ["-O2", "-c", "-w"]
# List of object files. This is populated using the list of source files,
# so there is no need to specify them explicitly.
object_files = []
prefix="/usr/local"
args = None
def parseProgramOptions():
"""
Parses the program options supplied to this script.
"""
global other_compiler_flags, other_link_flags, mma_kernel, prefix, args
parser = ap.ArgumentParser()
parser.add_argument('--prefix', help='The prefix of the installation path.' )
parser.add_argument('--libdir', help='The library directory.' )
parser.add_argument('--includedir', help='The include directory.' )
parser.add_argument('--mma_path',
help='The path to the math script or MathKernel binary. If "math" is in your path then there is not need for this option.' )
parser.add_argument('--clean', action='store_true',
help='Remove all files the build process created and exit.' )
parser.add_argument('--make', action='store_true',
help='Run make after generating the Makefile.' )
args = parser.parse_args()
if args.prefix is not None:
prefix = args.prefix
if args.libdir is not None:
other_link_flags.append('"-L' + args.libdir + '"')
if args.includedir is not None:
other_compiler_flags.append('"-I' + args.includedir + '"')
else:
other_compiler_flags.append('"-I' + prefix + '/include"')
if args.mma_path is not None:
mma_kernel = args.mma_path
# Convenience function, does what it says.
def deleteFileIfExists(file):
try:
os.remove(file)
except:
pass
# Wipes everything this script has created.
def clean(remove_binary = False):
global tmp_path
# Remove any .m files that may be around.
deleteFileIfExists(tmp_path + "/" + "MMACommand.m")
# Delete the generated Makefile.
deleteFileIfExists("Makefile")
# Remove the binary if we were asked to.
if remove_binary:
deleteFileIfExists(tmp_path + "/" + output_file)
# Delete object files and the copies of the source files we made.
for file in source_files:
short_name = ""
if file[-2:] == ".c":
short_name = file[:-2]
else:
short_name = file[:-4]
# Remove the object file.
deleteFileIfExists(tmp_path + "/" + short_name + ".o")
# Remove the source file...
deleteFileIfExists(tmp_path + "/" + file)
# ...and header.
deleteFileIfExists(tmp_path + "/" + short_name + ".h")
# Gets the directory of the Mathematica installation, sets the values of
# mma_install_path, wstp_path, library_path, and include_path.
def getMathematicaPaths():
global mma_install_path, wstp_path, mathlink_path, mma_link_path, library_path, include_path, link_libs, wstp
# Determine Mathematica's installation path.
mma_install_path = getMathematicaValue("$InstallationDirectory")
# Determine the Mathematica version.
mma_version = float(getMathematicaValue("$VersionNumber"))
if mma_version >= 10:
mma_link_path = wstp_path
wstp = True
else:
mma_link_path = mathlink_path
wstp = False
# Determine the static libraries to link the final binary with.
platform_system = platform.system()
if platform_system == "linux2":
if platform.processor() == "i386" or platform.processor() == "i586" or platform.processor() == "i686":
if wstp:
link_libs.append("WSTP32i4")
else:
link_libs.append("ML32i4")
link_libs.extend(["stdc++", "dl", "uuid"])
else:
if wstp:
link_libs.append("WSTP64i4")
else:
link_libs.append("ML64i4")
link_libs.extend(["stdc++", "dl", "uuid"])
elif platform_system == "Darwin":
if wstp:
link_libs.append("WSTPi4")
else:
link_libs.append("MLi4")
other_link_flags.append("-framework CoreFoundation")
elif platform_system == "win32":
# Untested and likely won't work.
if wstp:
link_libs.extend(["wstp32i4s", "Ws2_32.lib", "Rpcrt4.lib"])
else:
link_libs.extend(["ml32i4s", "Ws2_32.lib", "Rpcrt4.lib"])
elif platform_system == "win64":
# Untested and likely won't work.
if wstp:
link_libs.extend(["wstp64i4s", "Ws2_32.lib", "Rpcrt4.lib"])
else:
link_libs.extend(["ml64i4s", "Ws2_32.lib", "Rpcrt4.lib"])
# Find the include and library paths.
# According to: https://reference.wolfram.com/language/tutorial/WSTPDeveloperGuide-Unix.html
# "The WSTP Developer Kit (WSDK) is located in the directory
# $InstallationDirectory/SystemFiles/Links/WSTP/DeveloperKit/$SystemID within your Wolfram
# System directory."
system_id = getMathematicaValue("$SystemID")
if platform_system == "Darwin":
# This AlternativeLibraries directory contains versions of the WSTP
# libraries compiled with -stdlib=libc++ compiler flags. These libraries
# exist to link programs that need compatibility with Apple's newer
# libc++ C++ library.
library_path = "/%s/CompilerAdditions/AlternativeLibraries" % system_id
else:
library_path = "/%s/CompilerAdditions" % system_id
include_path = "/%s/CompilerAdditions" % system_id
def getMathematicaValue(value):
mma_command = "Print[%s]" % value
script_path = tmp_path + "/MMACommand.m"
# Delete the current script if it already exists.
try:
os.remove(script_path)
except OSError:
pass
# Write out the command.
with open(script_path, 'w') as f:
f.write(mma_command)
run_command = 'math -script "%s"' % script_path
result = subprocess.check_output(run_command, shell=True)
# Remove the terminal newline.
return result[:-1]
def main():
global output_path, mma_install_path
parseProgramOptions()
# Check if we are just asked to clean.
if args.clean is True:
clean(remove_binary=True)
return
# Set up build directory.
print "Build directory: " + tmp_path
if not os.path.exists(tmp_path):
os.makedirs(tmp_path)
# Now we ask Mathematica where it is installed.
try:
getMathematicaPaths()
except subprocess.CalledProcessError as e:
print "Return code: %d" % e.returncode
print "Is '%s' runnable from a command line?" % mma_kernel
clean()
return
print "Mathematica directory: " + mma_install_path
# print "Library Path: " + library_path
# print "Include Path: " + include_path
# Copy sources to the tmp_path directory.
if wstp:
print "Using WSTP."
shutil.copy("src/WSTP/mlbridge.cpp", tmp_path + "/mlbridge.cpp")
shutil.copy("src/WSTP/mlbridge.h", tmp_path + "/mlbridge.h")
else:
print "Using MathLink."
shutil.copy("src/MathLink/mlbridge.cpp", tmp_path + "/mlbridge.cpp")
shutil.copy("src/MathLink/mlbridge.h", tmp_path + "/mlbridge.h")
files = [f for f in os.listdir("src") if os.path.isfile("src/" + f)]
for file_name in files:
shutil.copy("src/" + file_name, tmp_path + "/" + file_name)
# Set the location of the final binary after build (not install).
if output_path == "":
output_path = tmp_path
# Compute the include path and library path.
other_compiler_flags.append('"-I' + mma_install_path + mma_link_path + include_path + '"')
other_link_flags.append('"-L' + mma_install_path + mma_link_path + library_path + '"')
other_link_flags.append(' '.join(["-l" + lib for lib in link_libs]) )
# Construct the makefile.
cflags = " ".join(other_compiler_flags)
lflags = " ".join(other_link_flags)
cpp_sources = " ".join([tmp_path + "/" + file for file in source_files if file[-4:]==".cpp"])
c_sources = " ".join([tmp_path + "/" + file for file in source_files if file[-2:]==".c"])
binary = output_path + "/" + output_file
template_dict = { "cflags": cflags,
"lflags": lflags,
"cpp_sources": cpp_sources,
"c_sources": c_sources,
"cpp_compiler": cpp_compiler,
"c_compiler": c_compiler,
"binary": binary,
"prefix": prefix
}
# print template_dict
# print makefile_template
makefile = makefile_template % template_dict
# Write out the makefile.
# First, delete the Makefile if it exists.
deleteFileIfExists("Makefile")
# Now write out the Makefile.
with open("Makefile", "w") as f:
f.write(makefile)
if args.make is True:
subprocess.check_call("make", shell=True)
clean()
print "Done."
if __name__ == "__main__":
main()