Added a panel with settings: - Building APK After Publish - to start the build after the project has been successfully published (False by default). Disabled if SDK path is not specified; - Emulator - list of installed emulators in Android Studio (AVD Manager). At the start of Blender, the list is always empty, to fill and update it, you must click the Refresh button. To start the emulator, if you wish, you need to press the "Start" button (the list of emulators is obtained with the emulator -list-avds command, the launch is performed with the emulator -avd [name] command). The "Start" button is disabled if the name of the emulator is not selected from the list; - Run Emulator After Building APK - launch the emulator after successfully building the APK file. Disabled if no APK build is installed or no emulator name selected. To perform these operations, you need to specify the ANDROID_SDK_ROOT environment variable, if it is not specified in the OS, then the "Android SDK Path" setting is read and set as the environment variable os.environ ['ANDROID_SDK_ROOT'] to perform operations. If no value is specified, then the user receives a corresponding message to the console. If the specified value is incorrect, then the user will receive messages from the corresponding programs.
715 lines
27 KiB
Python
Executable file
715 lines
27 KiB
Python
Executable file
import glob
|
|
import json
|
|
import os
|
|
import shutil
|
|
import time
|
|
import stat
|
|
import subprocess
|
|
import threading
|
|
import webbrowser
|
|
import shlex
|
|
|
|
import bpy
|
|
|
|
import arm.assets as assets
|
|
from arm.exporter import ArmoryExporter
|
|
import arm.lib.make_datas
|
|
import arm.lib.server
|
|
import arm.log as log
|
|
import arm.make_logic as make_logic
|
|
import arm.make_renderpath as make_renderpath
|
|
import arm.make_state as state
|
|
import arm.make_world as make_world
|
|
import arm.utils
|
|
import arm.write_data as write_data
|
|
|
|
scripts_mtime = 0 # Monitor source changes
|
|
profile_time = 0
|
|
|
|
def run_proc(cmd, done):
|
|
def fn(p, done):
|
|
p.wait()
|
|
if done != None:
|
|
done()
|
|
p = subprocess.Popen(cmd)
|
|
threading.Thread(target=fn, args=(p, done)).start()
|
|
return p
|
|
|
|
def compile_shader_pass(res, raw_shaders_path, shader_name, defs, make_variants):
|
|
os.chdir(raw_shaders_path + '/' + shader_name)
|
|
|
|
# Open json file
|
|
json_name = shader_name + '.json'
|
|
with open(json_name) as f:
|
|
json_file = f.read()
|
|
json_data = json.loads(json_file)
|
|
|
|
fp = arm.utils.get_fp_build()
|
|
arm.lib.make_datas.make(res, shader_name, json_data, fp, defs, make_variants)
|
|
|
|
path = fp + '/compiled/Shaders'
|
|
c = json_data['contexts'][0]
|
|
for s in ['vertex_shader', 'fragment_shader', 'geometry_shader', 'tesscontrol_shader', 'tesseval_shader']:
|
|
if s in c:
|
|
shutil.copy(c[s], path + '/' + c[s].split('/')[-1])
|
|
|
|
def remove_readonly(func, path, excinfo):
|
|
os.chmod(path, stat.S_IWRITE)
|
|
func(path)
|
|
|
|
def export_data(fp, sdk_path):
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
print('\n' + '_' * 10 + ' [Armory] Compiling ' + '_' * 10)
|
|
if wrd.arm_verbose_output:
|
|
print('\nArmory v{0} ({1})'.format(wrd.arm_version, wrd.arm_commit))
|
|
print('OS: ' + arm.utils.get_os() + ', Target: ' + state.target + ', GAPI: ' + arm.utils.get_gapi() + ', Blender: ' + bpy.app.version_string)
|
|
|
|
# Clean compiled variants if cache is disabled
|
|
build_dir = arm.utils.get_fp_build()
|
|
if not wrd.arm_cache_build:
|
|
if os.path.isdir(build_dir + '/debug/html5-resources'):
|
|
shutil.rmtree(build_dir + '/debug/html5-resources', onerror=remove_readonly)
|
|
if os.path.isdir(build_dir + '/krom-resources'):
|
|
shutil.rmtree(build_dir + '/krom-resources', onerror=remove_readonly)
|
|
if os.path.isdir(build_dir + '/debug/krom-resources'):
|
|
shutil.rmtree(build_dir + '/debug/krom-resources', onerror=remove_readonly)
|
|
if os.path.isdir(build_dir + '/windows-resources'):
|
|
shutil.rmtree(build_dir + '/windows-resources', onerror=remove_readonly)
|
|
if os.path.isdir(build_dir + '/linux-resources'):
|
|
shutil.rmtree(build_dir + '/linux-resources', onerror=remove_readonly)
|
|
if os.path.isdir(build_dir + '/osx-resources'):
|
|
shutil.rmtree(build_dir + '/osx-resources', onerror=remove_readonly)
|
|
if os.path.isdir(build_dir + '/compiled/Shaders'):
|
|
shutil.rmtree(build_dir + '/compiled/Shaders', onerror=remove_readonly)
|
|
|
|
raw_shaders_path = sdk_path + '/armory/Shaders/'
|
|
assets_path = sdk_path + '/armory/Assets/'
|
|
export_physics = bpy.data.worlds['Arm'].arm_physics != 'Disabled'
|
|
export_navigation = bpy.data.worlds['Arm'].arm_navigation != 'Disabled'
|
|
export_ui = bpy.data.worlds['Arm'].arm_ui != 'Disabled'
|
|
assets.reset()
|
|
|
|
# Build node trees
|
|
ArmoryExporter.import_traits = []
|
|
make_logic.build()
|
|
make_world.build()
|
|
make_renderpath.build()
|
|
|
|
# Export scene data
|
|
assets.embedded_data = sorted(list(set(assets.embedded_data)))
|
|
physics_found = False
|
|
navigation_found = False
|
|
ui_found = False
|
|
ArmoryExporter.compress_enabled = state.is_publish and wrd.arm_asset_compression
|
|
ArmoryExporter.optimize_enabled = state.is_publish and wrd.arm_optimize_data
|
|
if not os.path.exists(build_dir + '/compiled/Assets'):
|
|
os.makedirs(build_dir + '/compiled/Assets')
|
|
# have a "zoo" collection in the current scene
|
|
export_coll = bpy.data.collections.new("export_coll")
|
|
bpy.context.scene.collection.children.link(export_coll)
|
|
for scene in bpy.data.scenes:
|
|
if scene == bpy.context.scene: continue
|
|
for o in scene.collection.all_objects:
|
|
if o.type == "MESH" or o.type == "EMPTY":
|
|
if o.name not in export_coll.all_objects.keys():
|
|
export_coll.objects.link(o)
|
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
|
bpy.data.collections.remove(export_coll) # destroy "zoo" collection
|
|
|
|
for scene in bpy.data.scenes:
|
|
if scene.arm_export:
|
|
ext = '.lz4' if ArmoryExporter.compress_enabled else '.arm'
|
|
asset_path = build_dir + '/compiled/Assets/' + arm.utils.safestr(scene.name) + ext
|
|
ArmoryExporter.export_scene(bpy.context, asset_path, scene=scene, depsgraph=depsgraph)
|
|
if ArmoryExporter.export_physics:
|
|
physics_found = True
|
|
if ArmoryExporter.export_navigation:
|
|
navigation_found = True
|
|
if ArmoryExporter.export_ui:
|
|
ui_found = True
|
|
assets.add(asset_path)
|
|
|
|
if physics_found == False: # Disable physics if no rigid body is exported
|
|
export_physics = False
|
|
|
|
if navigation_found == False:
|
|
export_navigation = False
|
|
|
|
if ui_found == False:
|
|
export_ui = False
|
|
|
|
if wrd.arm_ui == 'Enabled':
|
|
export_ui = True
|
|
|
|
modules = []
|
|
if wrd.arm_audio == 'Enabled':
|
|
modules.append('audio')
|
|
if export_physics:
|
|
modules.append('physics')
|
|
if export_navigation:
|
|
modules.append('navigation')
|
|
if export_ui:
|
|
modules.append('ui')
|
|
|
|
defs = arm.utils.def_strings_to_array(wrd.world_defs)
|
|
cdefs = arm.utils.def_strings_to_array(wrd.compo_defs)
|
|
|
|
if wrd.arm_verbose_output:
|
|
print('Exported modules:', modules)
|
|
print('Shader flags:', defs)
|
|
print('Compositor flags:', cdefs)
|
|
print('Khafile flags:', assets.khafile_defs)
|
|
|
|
# Render path is configurable at runtime
|
|
has_config = wrd.arm_write_config or os.path.exists(arm.utils.get_fp() + '/Bundled/config.arm')
|
|
|
|
# Write compiled.inc
|
|
shaders_path = build_dir + '/compiled/Shaders'
|
|
if not os.path.exists(shaders_path):
|
|
os.makedirs(shaders_path)
|
|
write_data.write_compiledglsl(defs + cdefs, make_variants=has_config)
|
|
|
|
# Write referenced shader passes
|
|
if not os.path.isfile(build_dir + '/compiled/Shaders/shader_datas.arm') or state.last_world_defs != wrd.world_defs:
|
|
res = {'shader_datas': []}
|
|
|
|
for ref in assets.shader_passes:
|
|
# Ensure shader pass source exists
|
|
if not os.path.exists(raw_shaders_path + '/' + ref):
|
|
continue
|
|
assets.shader_passes_assets[ref] = []
|
|
if ref.startswith('compositor_pass'):
|
|
compile_shader_pass(res, raw_shaders_path, ref, defs + cdefs, make_variants=has_config)
|
|
else:
|
|
compile_shader_pass(res, raw_shaders_path, ref, defs, make_variants=has_config)
|
|
|
|
# Workaround to also export non-material world shaders
|
|
res['shader_datas'] += make_world.shader_datas
|
|
|
|
arm.utils.write_arm(shaders_path + '/shader_datas.arm', res)
|
|
|
|
for ref in assets.shader_passes:
|
|
for s in assets.shader_passes_assets[ref]:
|
|
assets.add_shader(shaders_path + '/' + s + '.glsl')
|
|
for file in assets.shaders_external:
|
|
name = file.split('/')[-1].split('\\')[-1]
|
|
target = build_dir + '/compiled/Shaders/' + name
|
|
if not os.path.exists(target):
|
|
shutil.copy(file, target)
|
|
state.last_world_defs = wrd.world_defs
|
|
|
|
# Reset path
|
|
os.chdir(fp)
|
|
|
|
# Copy std shaders
|
|
if not os.path.isdir(build_dir + '/compiled/Shaders/std'):
|
|
shutil.copytree(raw_shaders_path + 'std', build_dir + '/compiled/Shaders/std')
|
|
|
|
# Write config.arm
|
|
resx, resy = arm.utils.get_render_resolution(arm.utils.get_active_scene())
|
|
if wrd.arm_write_config:
|
|
write_data.write_config(resx, resy)
|
|
|
|
# Write khafile.js
|
|
enable_dce = state.is_publish and wrd.arm_dce
|
|
import_logic = not state.is_publish and arm.utils.logic_editor_space() != None
|
|
write_data.write_khafilejs(state.is_play, export_physics, export_navigation, export_ui, state.is_publish, enable_dce, ArmoryExporter.import_traits, import_logic)
|
|
|
|
# Write Main.hx - depends on write_khafilejs for writing number of assets
|
|
scene_name = arm.utils.get_project_scene_name()
|
|
write_data.write_mainhx(scene_name, resx, resy, state.is_play, state.is_publish)
|
|
if scene_name != state.last_scene or resx != state.last_resx or resy != state.last_resy:
|
|
wrd.arm_recompile = True
|
|
state.last_resx = resx
|
|
state.last_resy = resy
|
|
state.last_scene = scene_name
|
|
|
|
def compile(assets_only=False):
|
|
wrd = bpy.data.worlds['Arm']
|
|
fp = arm.utils.get_fp()
|
|
os.chdir(fp)
|
|
|
|
# Set build command
|
|
target_name = state.target
|
|
|
|
node_path = arm.utils.get_node_path()
|
|
khamake_path = arm.utils.get_khamake_path()
|
|
cmd = [node_path, khamake_path]
|
|
|
|
kha_target_name = arm.utils.get_kha_target(target_name)
|
|
if kha_target_name != '':
|
|
cmd.append(kha_target_name)
|
|
|
|
# Custom exporter
|
|
if state.is_export:
|
|
item = wrd.arm_exporterlist[wrd.arm_exporterlist_index]
|
|
if item.arm_project_target == 'custom' and item.arm_project_khamake != '':
|
|
for s in item.arm_project_khamake.split(' '):
|
|
cmd.append(s)
|
|
|
|
ffmpeg_path = arm.utils.get_ffmpeg_path() # Path to binary
|
|
if ffmpeg_path != '':
|
|
cmd.append('--ffmpeg')
|
|
cmd.append(ffmpeg_path) # '"' + ffmpeg_path + '"'
|
|
|
|
state.export_gapi = arm.utils.get_gapi()
|
|
cmd.append('-g')
|
|
cmd.append(state.export_gapi)
|
|
|
|
if arm.utils.get_legacy_shaders() or 'ios' in state.target:
|
|
if 'html5' in state.target or 'ios' in state.target:
|
|
pass
|
|
else:
|
|
cmd.append('--shaderversion')
|
|
cmd.append('110')
|
|
elif 'android' in state.target or 'html5' in state.target:
|
|
cmd.append('--shaderversion')
|
|
cmd.append('300')
|
|
else:
|
|
cmd.append('--shaderversion')
|
|
cmd.append('330')
|
|
|
|
if '_VR' in wrd.world_defs:
|
|
cmd.append('--vr')
|
|
cmd.append('webvr')
|
|
|
|
if arm.utils.get_rp().rp_renderer == 'Raytracer':
|
|
cmd.append('--raytrace')
|
|
cmd.append('dxr')
|
|
dxc_path = fp + '/HlslShaders/dxc.exe'
|
|
subprocess.Popen([dxc_path, '-Zpr', '-Fo', fp + '/Bundled/raytrace.cso', '-T', 'lib_6_3', fp + '/HlslShaders/raytrace.hlsl']).wait()
|
|
|
|
if arm.utils.get_khamake_threads() > 1:
|
|
cmd.append('--parallelAssetConversion')
|
|
cmd.append(str(arm.utils.get_khamake_threads()))
|
|
|
|
compilation_server = False
|
|
|
|
cmd.append('--to')
|
|
if (kha_target_name == 'krom' and not state.is_publish) or (kha_target_name == 'html5' and not state.is_publish):
|
|
cmd.append(arm.utils.build_dir() + '/debug')
|
|
# Start compilation server
|
|
if kha_target_name == 'krom' and arm.utils.get_compilation_server() and not assets_only and wrd.arm_cache_build:
|
|
compilation_server = True
|
|
arm.lib.server.run_haxe(arm.utils.get_haxe_path())
|
|
else:
|
|
cmd.append(arm.utils.build_dir())
|
|
|
|
if assets_only or compilation_server:
|
|
cmd.append('--nohaxe')
|
|
cmd.append('--noproject')
|
|
|
|
if not wrd.arm_verbose_output:
|
|
cmd.append("--quiet")
|
|
else:
|
|
print("Using project from " + arm.utils.get_fp())
|
|
print("Running: ", cmd)
|
|
|
|
state.proc_build = run_proc(cmd, assets_done if compilation_server else build_done)
|
|
|
|
def build(target, is_play=False, is_publish=False, is_export=False):
|
|
global profile_time
|
|
profile_time = time.time()
|
|
|
|
state.target = target
|
|
state.is_play = is_play
|
|
state.is_publish = is_publish
|
|
state.is_export = is_export
|
|
|
|
# Save blend
|
|
if arm.utils.get_save_on_build():
|
|
bpy.ops.wm.save_mainfile()
|
|
|
|
log.clear(clear_warnings=True)
|
|
|
|
# Set camera in active scene
|
|
active_scene = arm.utils.get_active_scene()
|
|
if active_scene.camera == None:
|
|
for o in active_scene.objects:
|
|
if o.type == 'CAMERA':
|
|
active_scene.camera = o
|
|
break
|
|
|
|
# Get paths
|
|
sdk_path = arm.utils.get_sdk_path()
|
|
raw_shaders_path = sdk_path + '/armory/Shaders/'
|
|
|
|
# Set dir
|
|
fp = arm.utils.get_fp()
|
|
os.chdir(fp)
|
|
|
|
# Create directories
|
|
wrd = bpy.data.worlds['Arm']
|
|
sources_path = 'Sources/' + arm.utils.safestr(wrd.arm_project_package)
|
|
if not os.path.exists(sources_path):
|
|
os.makedirs(sources_path)
|
|
|
|
# Save external scripts edited inside Blender
|
|
write_texts = False
|
|
for text in bpy.data.texts:
|
|
if text.filepath != '' and text.is_dirty:
|
|
write_texts = True
|
|
break
|
|
if write_texts:
|
|
area = bpy.context.area
|
|
old_type = area.type
|
|
area.type = 'TEXT_EDITOR'
|
|
for text in bpy.data.texts:
|
|
if text.filepath != '' and text.is_dirty and os.path.isfile(text.filepath):
|
|
area.spaces[0].text = text
|
|
bpy.ops.text.save()
|
|
area.type = old_type
|
|
|
|
# Save internal Haxe scripts
|
|
for text in bpy.data.texts:
|
|
if text.filepath == '' and text.name[-3:] == '.hx':
|
|
with open('Sources/' + arm.utils.safestr(wrd.arm_project_package) + '/' + text.name, 'w') as f:
|
|
f.write(text.as_string())
|
|
|
|
# Export data
|
|
export_data(fp, sdk_path)
|
|
|
|
if state.target == 'html5':
|
|
w, h = arm.utils.get_render_resolution(arm.utils.get_active_scene())
|
|
write_data.write_indexhtml(w, h, is_publish)
|
|
# Bundle files from include dir
|
|
if os.path.isdir('include'):
|
|
dest = '/html5/' if is_publish else '/debug/html5/'
|
|
for fn in glob.iglob(os.path.join('include', '**'), recursive=False):
|
|
shutil.copy(fn, arm.utils.build_dir() + dest + os.path.basename(fn))
|
|
|
|
def play_done():
|
|
state.proc_play = None
|
|
state.redraw_ui = True
|
|
log.clear()
|
|
|
|
def assets_done():
|
|
if state.proc_build == None:
|
|
return
|
|
result = state.proc_build.poll()
|
|
if result == 0:
|
|
# Connect to the compilation server
|
|
os.chdir(arm.utils.build_dir() + '/debug/')
|
|
cmd = [arm.utils.get_haxe_path(), '--connect', '6000', 'project-krom.hxml']
|
|
state.proc_build = run_proc(cmd, compilation_server_done)
|
|
else:
|
|
state.proc_build = None
|
|
state.redraw_ui = True
|
|
log.error('Build failed, check console')
|
|
|
|
def compilation_server_done():
|
|
if state.proc_build == None:
|
|
return
|
|
result = state.proc_build.poll()
|
|
if result == 0:
|
|
if os.path.exists('krom/krom.js'):
|
|
os.chmod('krom/krom.js', stat.S_IWRITE)
|
|
os.remove('krom/krom.js')
|
|
os.rename('krom/krom.js.temp', 'krom/krom.js')
|
|
build_done()
|
|
else:
|
|
state.proc_build = None
|
|
state.redraw_ui = True
|
|
log.error('Build failed, check console')
|
|
|
|
def build_done():
|
|
print('Finished in ' + str(time.time() - profile_time))
|
|
if log.num_warnings > 0:
|
|
log.print_warn(f'{log.num_warnings} warnings occurred during compilation')
|
|
if state.proc_build is None:
|
|
return
|
|
result = state.proc_build.poll()
|
|
state.proc_build = None
|
|
state.redraw_ui = True
|
|
if result == 0:
|
|
bpy.data.worlds['Arm'].arm_recompile = False
|
|
build_success()
|
|
else:
|
|
log.error('Build failed, check console')
|
|
|
|
def patch():
|
|
if state.proc_build != None:
|
|
return
|
|
assets.invalidate_enabled = False
|
|
fp = arm.utils.get_fp()
|
|
os.chdir(fp)
|
|
asset_path = arm.utils.get_fp_build() + '/compiled/Assets/' + arm.utils.safestr(bpy.context.scene.name) + '.arm'
|
|
ArmoryExporter.export_scene(bpy.context, asset_path, scene=bpy.context.scene)
|
|
if not os.path.isdir(arm.utils.build_dir() + '/compiled/Shaders/std'):
|
|
raw_shaders_path = arm.utils.get_sdk_path() + '/armory/Shaders/'
|
|
shutil.copytree(raw_shaders_path + 'std', arm.utils.build_dir() + '/compiled/Shaders/std')
|
|
node_path = arm.utils.get_node_path()
|
|
khamake_path = arm.utils.get_khamake_path()
|
|
|
|
cmd = [node_path, khamake_path, 'krom']
|
|
cmd.extend(('--shaderversion', '330', '--parallelAssetConversion', '4',
|
|
'--to', arm.utils.build_dir() + '/debug', '--nohaxe', '--noproject'))
|
|
|
|
assets.invalidate_enabled = True
|
|
state.proc_build = run_proc(cmd, patch_done)
|
|
|
|
def patch_done():
|
|
js = 'iron.Scene.patch();'
|
|
write_patch(js)
|
|
state.proc_build = None
|
|
|
|
patch_id = 0
|
|
|
|
def write_patch(js):
|
|
global patch_id
|
|
with open(arm.utils.get_fp_build() + '/debug/krom/krom.patch', 'w') as f:
|
|
patch_id += 1
|
|
f.write(str(patch_id) + '\n')
|
|
f.write(js)
|
|
|
|
def runtime_to_target():
|
|
wrd = bpy.data.worlds['Arm']
|
|
if wrd.arm_runtime == 'Krom':
|
|
return 'krom'
|
|
else:
|
|
return 'html5'
|
|
|
|
def get_khajs_path(target):
|
|
if target == 'krom':
|
|
return arm.utils.build_dir() + '/debug/krom/krom.js'
|
|
else: # Browser
|
|
return arm.utils.build_dir() + '/debug/html5/kha.js'
|
|
|
|
def play():
|
|
global scripts_mtime
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
build(target=runtime_to_target(), is_play=True)
|
|
|
|
khajs_path = get_khajs_path(state.target)
|
|
if not wrd.arm_cache_build or \
|
|
not os.path.isfile(khajs_path) or \
|
|
assets.khafile_defs_last != assets.khafile_defs or \
|
|
state.last_target != state.target:
|
|
wrd.arm_recompile = True
|
|
|
|
state.last_target = state.target
|
|
|
|
# Trait sources modified
|
|
state.mod_scripts = []
|
|
script_path = arm.utils.get_fp() + '/Sources/' + arm.utils.safestr(wrd.arm_project_package)
|
|
if os.path.isdir(script_path):
|
|
new_mtime = scripts_mtime
|
|
for fn in glob.iglob(os.path.join(script_path, '**', '*.hx'), recursive=True):
|
|
mtime = os.path.getmtime(fn)
|
|
if scripts_mtime < mtime:
|
|
arm.utils.fetch_script_props(fn) # Trait props
|
|
fn = fn.split('Sources/')[1]
|
|
fn = fn[:-3] #.hx
|
|
fn = fn.replace('/', '.')
|
|
state.mod_scripts.append(fn)
|
|
wrd.arm_recompile = True
|
|
if new_mtime < mtime:
|
|
new_mtime = mtime
|
|
scripts_mtime = new_mtime
|
|
if len(state.mod_scripts) > 0: # Trait props
|
|
arm.utils.fetch_trait_props()
|
|
|
|
compile(assets_only=(not wrd.arm_recompile))
|
|
|
|
def build_success():
|
|
log.clear()
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
if state.is_play:
|
|
if wrd.arm_runtime == 'Browser':
|
|
# Start server
|
|
os.chdir(arm.utils.get_fp())
|
|
t = threading.Thread(name='localserver', target=arm.lib.server.run_tcp)
|
|
t.daemon = True
|
|
t.start()
|
|
html5_app_path = 'http://localhost:8040/' + arm.utils.build_dir() + '/debug/html5'
|
|
webbrowser.open(html5_app_path)
|
|
elif wrd.arm_runtime == 'Krom':
|
|
if wrd.arm_live_patch:
|
|
open(arm.utils.get_fp_build() + '/debug/krom/krom.patch', 'w').close()
|
|
krom_location, krom_path = arm.utils.krom_paths()
|
|
os.chdir(krom_location)
|
|
cmd = [krom_path, arm.utils.get_fp_build() + '/debug/krom', arm.utils.get_fp_build() + '/debug/krom-resources']
|
|
if arm.utils.get_os() == 'win':
|
|
cmd.append('--consolepid')
|
|
cmd.append(str(os.getpid()))
|
|
if wrd.arm_audio == 'Disabled':
|
|
cmd.append('--nosound')
|
|
state.proc_play = run_proc(cmd, play_done)
|
|
|
|
elif state.is_publish:
|
|
sdk_path = arm.utils.get_sdk_path()
|
|
target_name = arm.utils.get_kha_target(state.target)
|
|
files_path = os.path.join(arm.utils.get_fp_build(), target_name)
|
|
|
|
if (target_name == 'html5' or target_name == 'krom') and wrd.arm_minify_js:
|
|
# Minify JS
|
|
minifier_path = sdk_path + '/lib/armory_tools/uglifyjs/bin/uglifyjs'
|
|
if target_name == 'html5':
|
|
jsfile = files_path + '/kha.js'
|
|
else:
|
|
jsfile = files_path + '/krom.js'
|
|
args = [arm.utils.get_node_path(), minifier_path, jsfile, '-o', jsfile]
|
|
proc = subprocess.Popen(args)
|
|
proc.wait()
|
|
|
|
if target_name == 'krom':
|
|
# Copy Krom binaries
|
|
if state.target == 'krom-windows':
|
|
gapi = state.export_gapi
|
|
ext = '' if gapi == 'direct3d11' else '_' + gapi
|
|
krom_location = sdk_path + '/Krom/Krom' + ext + '.exe'
|
|
shutil.copy(krom_location, files_path + '/Krom.exe')
|
|
krom_exe = arm.utils.safestr(wrd.arm_project_name) + '.exe'
|
|
os.rename(files_path + '/Krom.exe', files_path + '/' + krom_exe)
|
|
elif state.target == 'krom-linux':
|
|
krom_location = sdk_path + '/Krom/Krom'
|
|
shutil.copy(krom_location, files_path)
|
|
krom_exe = arm.utils.safestr(wrd.arm_project_name)
|
|
os.rename(files_path + '/Krom', files_path + '/' + krom_exe)
|
|
krom_exe = './' + krom_exe
|
|
else:
|
|
krom_location = sdk_path + '/Krom/Krom.app'
|
|
shutil.copytree(krom_location, files_path + '/Krom.app')
|
|
game_files = os.listdir(files_path)
|
|
for f in game_files:
|
|
f = files_path + '/' + f
|
|
if os.path.isfile(f):
|
|
shutil.move(f, files_path + '/Krom.app/Contents/MacOS')
|
|
krom_exe = arm.utils.safestr(wrd.arm_project_name) + '.app'
|
|
os.rename(files_path + '/Krom.app', files_path + '/' + krom_exe)
|
|
|
|
# Rename
|
|
ext = state.target.split('-')[-1] # krom-windows
|
|
new_files_path = files_path + '-' + ext
|
|
os.rename(files_path, new_files_path)
|
|
files_path = new_files_path
|
|
|
|
if target_name == 'html5':
|
|
project_path = files_path
|
|
print('Exported HTML5 package to ' + project_path)
|
|
elif target_name.startswith('ios') or target_name.startswith('osx'): # TODO: to macos
|
|
project_path = files_path + '-build'
|
|
print('Exported XCode project to ' + project_path)
|
|
elif target_name.startswith('windows'):
|
|
project_path = files_path + '-build'
|
|
print('Exported Visual Studio 2017 project to ' + project_path)
|
|
elif target_name.startswith('android'):
|
|
project_path = os.path.join(files_path + '-build', arm.utils.safestr(wrd.arm_project_name))
|
|
print('Exported Android Studio project to ' + project_path)
|
|
elif target_name.startswith('krom'):
|
|
project_path = files_path
|
|
print('Exported Krom package to ' + project_path)
|
|
else:
|
|
project_path = files_path + '-build'
|
|
print('Exported makefiles to ' + project_path)
|
|
|
|
if arm.utils.get_arm_preferences().open_build_directory:
|
|
arm.utils.open_folder(project_path)
|
|
|
|
# Android build APK
|
|
if (arm.utils.get_project_android_build_apk()) and (len(arm.utils.get_android_sdk_root_path()) > 0):
|
|
print("\nBuilding APK")
|
|
# Check settings
|
|
path_sdk = arm.utils.get_android_sdk_root_path()
|
|
if len(path_sdk) > 0:
|
|
# Check Environment Variables - ANDROID_SDK_ROOT
|
|
if os.getenv('ANDROID_SDK_ROOT') == None:
|
|
# Set value from settings
|
|
os.environ['ANDROID_SDK_ROOT'] = path_sdk
|
|
else:
|
|
project_path = ''
|
|
|
|
# Build start
|
|
if len(project_path) > 0:
|
|
os.chdir(project_path) # set work folder
|
|
if arm.utils.get_os_is_windows():
|
|
state.proc_publish_build = run_proc(os.path.join(project_path, "gradlew.bat assembleDebug"), done_gradlew_build)
|
|
else:
|
|
cmd = shlex.split(os.path.join(project_path, "gradlew assembleDebug"))
|
|
state.proc_publish_build = run_proc(cmd, done_gradlew_build)
|
|
else:
|
|
print('\nBuilding APK Warning: ANDROID_SDK_ROOT is not specified in environment variables and "Android SDK Path" setting is not specified in preferences: \n- If you specify an environment variable ANDROID_SDK_ROOT, then you need to restart Blender;\n- If you specify the setting "Android SDK Path" in the preferences, then repeat operation "Publish"')
|
|
|
|
def done_gradlew_build():
|
|
if state.proc_publish_build == None:
|
|
return
|
|
result = state.proc_publish_build.poll()
|
|
if result == 0:
|
|
state.proc_publish_build = None
|
|
|
|
wrd = bpy.data.worlds['Arm']
|
|
path_apk = os.path.join(arm.utils.get_fp_build(), arm.utils.get_kha_target(state.target))
|
|
path_apk = os.path.join(path_apk + '-build', arm.utils.safestr(wrd.arm_project_name), "app", "build", "outputs", "apk", "debug")
|
|
|
|
print("\nBuild APK to " + path_apk)
|
|
# Open directory with APK
|
|
if arm.utils.get_android_open_build_apk_directory():
|
|
arm.utils.open_folder(path_apk)
|
|
# Running emulator
|
|
if wrd.arm_project_android_run_avd:
|
|
run_android_emulators(arm.utils.get_android_emulator_name())
|
|
state.redraw_ui = True
|
|
else:
|
|
state.proc_publish_build = None
|
|
state.redraw_ui = True
|
|
os.environ['ANDROID_SDK_ROOT'] = ''
|
|
log.error('Building the APK failed, check console')
|
|
|
|
def run_android_emulators(avd_name):
|
|
if len(avd_name.strip()) == 0:
|
|
return
|
|
print('\nRunning Emulator "'+ avd_name +'"')
|
|
path_file = arm.utils.get_android_emulator_file()
|
|
if len(path_file) > 0:
|
|
if arm.utils.get_os_is_windows():
|
|
run_proc(path_file + " -avd "+ avd_name, None)
|
|
else:
|
|
cmd = shlex.split(path_file + " -avd "+ avd_name)
|
|
run_proc(cmd, None)
|
|
else:
|
|
print('Update List Emulators Warning: File "'+ path_file +'" not found. Check that the variable ANDROID_SDK_ROOT is correct in environment variables or in "Android SDK Path" setting: \n- If you specify an environment variable ANDROID_SDK_ROOT, then you need to restart Blender;\n- If you specify the setting "Android SDK Path", then repeat operation "Publish"')
|
|
|
|
def clean():
|
|
os.chdir(arm.utils.get_fp())
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
# Remove build and compiled data
|
|
try:
|
|
if os.path.isdir(arm.utils.build_dir()):
|
|
shutil.rmtree(arm.utils.build_dir(), onerror=remove_readonly)
|
|
if os.path.isdir(arm.utils.get_fp() + '/build'): # Kode Studio build dir
|
|
shutil.rmtree(arm.utils.get_fp() + '/build', onerror=remove_readonly)
|
|
except:
|
|
print('Armory Warning: Some files in the build folder are locked')
|
|
|
|
# Remove compiled nodes
|
|
pkg_dir = arm.utils.safestr(wrd.arm_project_package).replace('.', '/')
|
|
nodes_path = 'Sources/' + pkg_dir + '/node/'
|
|
if os.path.isdir(nodes_path):
|
|
shutil.rmtree(nodes_path, onerror=remove_readonly)
|
|
|
|
# Remove khafile/Main.hx
|
|
if os.path.isfile('khafile.js'):
|
|
os.remove('khafile.js')
|
|
if os.path.isfile('Sources/Main.hx'):
|
|
os.remove('Sources/Main.hx')
|
|
|
|
# Remove Sources/ dir if empty
|
|
if os.path.exists('Sources/' + pkg_dir) and os.listdir('Sources/' + pkg_dir) == []:
|
|
shutil.rmtree('Sources/' + pkg_dir, onerror=remove_readonly)
|
|
if os.path.exists('Sources') and os.listdir('Sources') == []:
|
|
shutil.rmtree('Sources/', onerror=remove_readonly)
|
|
|
|
# To recache signatures for batched materials
|
|
for mat in bpy.data.materials:
|
|
mat.signature = ''
|
|
mat.arm_cached = False
|
|
|
|
# Restart compilation server
|
|
if arm.utils.get_compilation_server():
|
|
arm.lib.server.kill_haxe()
|
|
|
|
print('Project cleaned')
|