forked from JacquesLucke/animation_nodes
-
Notifications
You must be signed in to change notification settings - Fork 0
/
setup.py
232 lines (189 loc) · 7.5 KB
/
setup.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
import os
import sys
import json
import textwrap
import subprocess
from pprint import pprint
currentDirectory = os.path.dirname(os.path.abspath(__file__))
if not os.path.samefile(currentDirectory, os.getcwd()):
print("You are not in the correct directory.")
print("Expected:", currentDirectory)
print("Got: ", os.getcwd())
sys.exit()
if currentDirectory not in sys.path:
sys.path.append(currentDirectory)
from _setuputils.generic import *
from _setuputils.addon_files import *
from _setuputils.cythonize import execute_Cythonize
from _setuputils.compilation import execute_Compile
from _setuputils.copy_addon import execute_CopyAddon
from _setuputils.pypreprocess import execute_PyPreprocess
from _setuputils.setup_info_files import getSetupInfoList
from _setuputils.export import execute_Export, execute_ExportC
from _setuputils.compile_libraries import execute_CompileLibraries
addonName = "animation_nodes"
addonDirectory = os.path.join(currentDirectory, addonName)
defaultConfigPath = os.path.join(currentDirectory, "conf.default.json")
configPath = os.path.join(currentDirectory, "conf.json")
initPath = os.path.join(currentDirectory, addonName, "__init__.py")
if onLinux: currentOS = "linux"
elif onWindows: currentOS = "windows"
elif onMacOS: currentOS = "macOS"
addonVersion = getAddonVersion(initPath)
exportName = "{}_v{}_{}_{}_py{}{}".format(
addonName, *addonVersion[:2], currentOS, *sys.version_info[:2])
exportPath = os.path.join(currentDirectory, exportName + ".zip")
exportCPath = os.path.join(currentDirectory, "{}_c.zip".format(addonName))
exportCSetupPath = os.path.join(currentDirectory, "_export_c_setup.py")
possibleCommands = ["build", "help", "clean"]
buildOptionDescriptions = [
("--copy", "Copy build to location specified in the conf.json file"),
("--export", "Create installable .zip file"),
("--exportc", "Create build that can be compiled without cython"),
("--nocompile", "Don't compile the extension modules"),
("--noversioncheck", "Don't check the used Python version")
]
buildOptions = {option for option, _ in buildOptionDescriptions}
helpNote = "Use 'python setup.py help' to see the possible commands."
# Main
####################################################
def main():
configs = setupConfigFile()
args = sys.argv[1:]
if len(args) == 0 or args[0] == "help":
main_Help()
elif args[0] == "build":
main_Build(args[1:], configs)
elif args[0] == "clean":
main_Clean()
else:
print("Invalid command: '{}'\n".format(args[0]))
print(helpNote)
sys.exit()
def setupConfigFile():
if not fileExists(defaultConfigPath):
print("Expected conf.default.json file to exist.")
sys.exit()
if not fileExists(configPath):
copyFile(defaultConfigPath, configPath)
print(textwrap.dedent('''\
Copied the conf.default.json file to conf.json.
Please change it manually if needed.
Note: git ignorers it, so depending on the settings of your editor
it might not be shows inside it.
'''))
return readJsonFile(configPath)
# Help
####################################################
def main_Help():
print(textwrap.dedent('''\
usage: python setup.py <command> [options]
Possible commands:
help Show this help
build Build the addon from sources
clean Remove all untracked files except conf.py
The 'build' command has multiple options:'''))
for option, description in buildOptionDescriptions:
print(" {:20}{}".format(option, description))
# Clean
####################################################
def main_Clean():
answer = input("Remove all files? [y/N] ").lower()
print()
if answer == "y":
removedFiles = removeUntrackedFiles(filesToKeep = ["conf.json"])["removed"]
print("Cleanup Finished.")
print("Removed {} files.".format(len(removedFiles)))
else:
print("Operation canceled.")
sys.exit()
@returnChangedFileStates(currentDirectory)
def removeUntrackedFiles(filesToKeep):
storedFiles = {}
for path in filesToKeep:
if fileExists(path):
storedFiles[path] = readBinaryFile(path)
try:
pipe = subprocess.PIPE
subprocess.run(["git", "clean", "-fdx"], stdout = pipe, stderr = pipe)
except FileNotFoundError:
print("git is required but not installed")
sys.exit()
for path, content in storedFiles.items():
writeBinaryFile(path, content)
# Build
####################################################
def main_Build(options, configs):
checkBuildEnvironment(
checkCython = True,
checkPython = "--noversioncheck" not in options and "--nocompile" not in options
)
checkBuildOptions(options)
changedFileStates = build(skipCompilation = "--nocompile" in options)
printChangedFileStates(changedFileStates, currentDirectory)
if "--copy" in options:
copyTarget = configs["Copy Target"]
if not directoryExists(copyTarget):
print("Copy Target not found. Please correct the conf.json file.")
else:
execute_CopyAddon(addonDirectory, configs["Copy Target"], addonName)
print()
if "--export" in options:
execute_Export(addonDirectory, exportPath, addonName)
if "--exportc" in options:
execute_ExportC(addonDirectory, exportCPath, exportCSetupPath, addonName)
def printChangedFileStates(states, basepath):
printHeader("File System Changes")
print("New Files:")
printIndentedPathList(states["new"], basepath)
print("\nRemoved Files:")
printIndentedPathList(states["removed"], basepath)
print("\nModified Files:")
printIndentedPathList(states["changed"], basepath)
def printIndentedPathList(paths, basepath):
if len(paths) == 0:
print(" <none>")
else:
for path in sorted(paths):
print(" {}".format(os.path.relpath(path, basepath)))
@returnChangedFileStates(currentDirectory)
def build(skipCompilation = False):
setupInfoList = getSetupInfoList(addonDirectory)
execute_PyPreprocess(setupInfoList, addonDirectory)
execute_Cythonize(setupInfoList, addonDirectory)
if not skipCompilation:
execute_CompileLibraries(setupInfoList, addonDirectory)
execute_Compile(setupInfoList, addonDirectory)
def checkBuildEnvironment(checkCython, checkPython):
if checkCython:
try: import Cython
except:
print("Cython is not installed for this Python version.")
print(sys.version)
sys.exit()
if checkPython:
v = sys.version_info
if v.major != 3 or v.minor != 7:
print(textwrap.dedent('''\
Blender 2.8 officially uses Python 3.7.x.
You are using: {}
Use the --noversioncheck option to disable this check.\
'''.format(sys.version)))
sys.exit()
def checkBuildOptions(options):
options = set(options)
invalidOptions = options - buildOptions
if len(invalidOptions) > 0:
print("Invalid build options: {}\n".format(invalidOptions))
print(helpNote)
sys.exit()
if "--nocompile" in options:
if "--copy" in options:
print("The options --nocompile and --copy don't work together.")
sys.exit()
if "--export" in options:
print("The options --nocompile and --export don't work together.")
sys.exit()
# Run Main
###############################################
main()