Allow Linux users to build Android even if Python 2 is their default Python

After this commit, users who have the `python` executable as Python 2 can do simply:

    cd android/
    ./build-all.py
    ./build.py

Previously, the following would fail:

    python3 build-all.py

since build-all.py did subcalls to `python build.py`.

Remove install-all.py as it is redundant with `build-all.py -deploy`,
instead of fixing it as well.

Introduce argparse since we are separating argument parsing out of
the `main()` function.

Tested in Ubuntu 16.04, behaviour should be unchanged for Windows.
This commit is contained in:
Ciro Santilli 2017-10-20 15:41:11 +01:00
parent 542be437da
commit 848310ffdb
6 changed files with 142 additions and 139 deletions

View file

@ -39,7 +39,7 @@ This will build all apks and puts them into the **bin** folder.
#### Build and deploy #### Build and deploy
``` ```
install-all.py build-all.py -deploy
``` ```
This will build all apks and deploys them to the currently attached android device. This will build all apks and deploys them to the currently attached android device.

19
android/build-all.py Normal file → Executable file
View file

@ -1,8 +1,11 @@
# Build all examples #!/usr/bin/env python3
# Pass -deploy to also install on connected device
import argparse
import subprocess import subprocess
import sys import sys
import build
EXAMPLES = [ EXAMPLES = [
"bloom", "bloom",
"computecullandlod", "computecullandlod",
@ -64,18 +67,16 @@ COLOR_END = '\033[0m'
CURR_INDEX = 0 CURR_INDEX = 0
BUILD_ARGUMENTS = "" parser = argparse.ArgumentParser()
for arg in sys.argv[1:]: parser.add_argument('-deploy', default=False, action='store_true', help="install examples on device")
if arg == "-deploy": parser.add_argument('-validation', default=False, action='store_true')
BUILD_ARGUMENTS += "-deploy" args = parser.parse_args()
if arg == "-validation":
BUILD_ARGUMENTS += "-validation"
print("Building all examples...") print("Building all examples...")
for example in EXAMPLES: for example in EXAMPLES:
print(COLOR_GREEN + "Building %s (%d/%d)" % (example, CURR_INDEX, len(EXAMPLES)) + COLOR_END) print(COLOR_GREEN + "Building %s (%d/%d)" % (example, CURR_INDEX, len(EXAMPLES)) + COLOR_END)
if subprocess.call(("python build.py %s %s" % (example, BUILD_ARGUMENTS)).split(' ')) != 0: if not build.main(example, args.deploy, args.validation):
print("Error during build process for %s" % example) print("Error during build process for %s" % example)
sys.exit(-1) sys.exit(-1)
CURR_INDEX += 1 CURR_INDEX += 1

239
android/build.py Normal file → Executable file
View file

@ -1,142 +1,153 @@
# Single example build and deploy script #!/usr/bin/env python3
import os import os
import subprocess import subprocess
import sys import sys
import shutil import shutil
import glob import glob
import json import json
import argparse
# Android SDK version used def main(project_folder, deploy=False, validation=False):
SDK_VERSION = "android-23"
PROJECT_FOLDER = "" # Android SDK version used
SDK_VERSION = "android-23"
# Check if python 3, python 2 not supported # Check if python 3, python 2 not supported
if sys.version_info <= (3, 0): if sys.version_info <= (3, 0):
print("Sorry, requires Python 3.x, not Python 2.x") print("Sorry, requires Python 3.x, not Python 2.x")
sys.exit(-1) return False
# Name/folder of the project to build # Definitions (apk name, folders, etc.) will be taken from a json definition
if len(sys.argv) > 1: if not os.path.isfile(os.path.join(project_folder, "example.json")):
PROJECT_FOLDER = sys.argv[1] print("Could not find json definition for example %s" % project_folder)
if not os.path.exists(PROJECT_FOLDER): return False
print("Please specify a valid project folder to build!")
sys.exit(-1)
# Definitions (apk name, folders, etc.) will be taken from a json definition # Check if a build file is present, if not create one using the android SDK version specified
if not os.path.isfile(os.path.join(PROJECT_FOLDER, "example.json")): if not os.path.isfile(os.path.join(project_folder, "build.xml")):
print("Could not find json definition for example %s" % PROJECT_FOLDER) print("Build.xml not present, generating with %s " % SDK_VERSION)
sys.exit(-1) ANDROID_CMD = "android"
if os.name == 'nt':
ANDROID_CMD += ".bat"
if subprocess.call(("%s update project -p ./%s -t %s" % (ANDROID_CMD, project_folder, SDK_VERSION)).split(' ')) != 0:
print("Error: Project update failed!")
return False
# Check if a build file is present, if not create one using the android SDK version specified # Load example definition from json file
if not os.path.isfile(os.path.join(PROJECT_FOLDER, "build.xml")): with open(os.path.join(project_folder, "example.json")) as json_file:
print("Build.xml not present, generating with %s " % SDK_VERSION) EXAMPLE_JSON = json.load(json_file)
ANDROID_CMD = "android"
if os.name == 'nt':
ANDROID_CMD += ".bat"
if subprocess.call(("%s update project -p ./%s -t %s" % (ANDROID_CMD, PROJECT_FOLDER, SDK_VERSION)).split(' ')) != 0:
print("Error: Project update failed!")
sys.exit(-1)
# Load example definition from json file APK_NAME = EXAMPLE_JSON["apkname"]
with open(os.path.join(PROJECT_FOLDER, "example.json")) as json_file: SHADER_DIR = EXAMPLE_JSON["directories"]["shaders"]
EXAMPLE_JSON = json.load(json_file)
APK_NAME = EXAMPLE_JSON["apkname"] # Additional
SHADER_DIR = EXAMPLE_JSON["directories"]["shaders"] ADDITIONAL_DIRS = []
ADDITIONAL_FILES = []
if "assets" in EXAMPLE_JSON and "additional" in EXAMPLE_JSON["assets"]:
ADDITIONAL = EXAMPLE_JSON["assets"]["additional"]
if "directories" in ADDITIONAL:
ADDITIONAL_DIRS = ADDITIONAL["directories"]
if "files" in ADDITIONAL:
ADDITIONAL_FILES = ADDITIONAL["files"]
# Additional # Get assets to be copied
ADDITIONAL_DIRS = [] ASSETS_MODELS = []
ADDITIONAL_FILES = [] ASSETS_TEXTURES = []
if "assets" in EXAMPLE_JSON and "additional" in EXAMPLE_JSON["assets"]: if "assets" in EXAMPLE_JSON:
ADDITIONAL = EXAMPLE_JSON["assets"]["additional"] ASSETS = EXAMPLE_JSON["assets"]
if "directories" in ADDITIONAL: if "models" in ASSETS:
ADDITIONAL_DIRS = ADDITIONAL["directories"] ASSETS_MODELS = EXAMPLE_JSON["assets"]["models"]
if "files" in ADDITIONAL: if "textures" in ASSETS:
ADDITIONAL_FILES = ADDITIONAL["files"] ASSETS_TEXTURES = EXAMPLE_JSON["assets"]["textures"]
# Get assets to be copied # Enable validation
ASSETS_MODELS = [] BUILD_ARGS = ""
ASSETS_TEXTURES = []
if "assets" in EXAMPLE_JSON:
ASSETS = EXAMPLE_JSON["assets"]
if "models" in ASSETS:
ASSETS_MODELS = EXAMPLE_JSON["assets"]["models"]
if "textures" in ASSETS:
ASSETS_TEXTURES = EXAMPLE_JSON["assets"]["textures"]
# Enable validation if validation:
VALIDATION = False
BUILD_ARGS = ""
for arg in sys.argv[1:]:
if arg == "-validation":
VALIDATION = True
# Use a define to force validation in code # Use a define to force validation in code
BUILD_ARGS = "APP_CFLAGS=-D_VALIDATION" BUILD_ARGS = "APP_CFLAGS=-D_VALIDATION"
break
# Verify submodules are loaded in external folder # Verify submodules are loaded in external folder
if not os.listdir("../external/glm/") or not os.listdir("../external/gli/"): if not os.listdir("../external/glm/") or not os.listdir("../external/gli/"):
print("External submodules not loaded. Clone them using:") print("External submodules not loaded. Clone them using:")
print("\tgit submodule init\n\tgit submodule update") print("\tgit submodule init\n\tgit submodule update")
sys.exit(-1) return False
# Build # Build
os.chdir(PROJECT_FOLDER) old_cwd = os.getcwd()
os.chdir(project_folder)
if subprocess.call("ndk-build %s" %BUILD_ARGS, shell=True) == 0: if subprocess.call("ndk-build %s" %BUILD_ARGS, shell=True) == 0:
print("Build successful") print("Build successful")
if VALIDATION: if validation:
# Copy validation layers # Copy validation layers
# todo: Currently only arm v7 # todo: Currently only arm v7
print("Validation enabled, copying validation layers...") print("Validation enabled, copying validation layers...")
os.makedirs("./libs/armeabi-v7a", exist_ok=True) os.makedirs("./libs/armeabi-v7a", exist_ok=True)
for file in glob.glob("../layers/armeabi-v7a/*.so"): for file in glob.glob("../layers/armeabi-v7a/*.so"):
print("\t" + file) print("\t" + file)
shutil.copy(file, "./libs/armeabi-v7a") shutil.copy(file, "./libs/armeabi-v7a")
# Create folders # Create folders
os.makedirs("./assets/shaders/base", exist_ok=True) os.makedirs("./assets/shaders/base", exist_ok=True)
os.makedirs("./assets/shaders/%s" % SHADER_DIR, exist_ok=True) os.makedirs("./assets/shaders/%s" % SHADER_DIR, exist_ok=True)
os.makedirs("./assets/models", exist_ok=True) os.makedirs("./assets/models", exist_ok=True)
os.makedirs("./assets/textures", exist_ok=True) os.makedirs("./assets/textures", exist_ok=True)
for directory in ADDITIONAL_DIRS: for directory in ADDITIONAL_DIRS:
os.makedirs("./assets/%s" % directory, exist_ok=True) os.makedirs("./assets/%s" % directory, exist_ok=True)
os.makedirs("./res/drawable", exist_ok=True) os.makedirs("./res/drawable", exist_ok=True)
for filename in glob.glob("../../data/shaders/base/*.spv"): for filename in glob.glob("../../data/shaders/base/*.spv"):
shutil.copy(filename, "./assets/shaders/base") shutil.copy(filename, "./assets/shaders/base")
for filename in glob.glob("../../data/shaders/%s/*.spv" %SHADER_DIR): for filename in glob.glob("../../data/shaders/%s/*.spv" %SHADER_DIR):
shutil.copy(filename, "./assets/shaders/%s" % SHADER_DIR) shutil.copy(filename, "./assets/shaders/%s" % SHADER_DIR)
for filename in ASSETS_MODELS: for filename in ASSETS_MODELS:
shutil.copy("../../data/models/%s" % filename, "./assets/models") shutil.copy("../../data/models/%s" % filename, "./assets/models")
for filename in ASSETS_TEXTURES: for filename in ASSETS_TEXTURES:
shutil.copy("../../data/textures/%s" % filename, "./assets/textures") shutil.copy("../../data/textures/%s" % filename, "./assets/textures")
for filename in ADDITIONAL_FILES: for filename in ADDITIONAL_FILES:
if "*." in filename: if "*." in filename:
for fname in glob.glob("../../data/%s" % filename): for fname in glob.glob("../../data/%s" % filename):
locfname = fname.replace("../../data/", "") locfname = fname.replace("../../data/", "")
shutil.copy(fname, "./assets/%s" % locfname) shutil.copy(fname, "./assets/%s" % locfname)
else:
shutil.copy("../../data/%s" % filename, "./assets/%s" % filename)
shutil.copy("../../android/images/icon.png", "./res/drawable")
if subprocess.call("ant debug -Dout.final.file=%s.apk" % APK_NAME, shell=True) == 0:
if deploy and subprocess.call("adb install -r %s.apk" % APK_NAME, shell=True) != 0:
print("Could not deploy to device!")
else: else:
shutil.copy("../../data/%s" % filename, "./assets/%s" % filename) print("Error during build process!")
return False
shutil.copy("../../android/images/icon.png", "./res/drawable")
if subprocess.call("ant debug -Dout.final.file=%s.apk" % APK_NAME, shell=True) == 0:
# Deploy to device
for arg in sys.argv[1:]:
if arg == "-deploy":
if subprocess.call("adb install -r %s.apk" % APK_NAME, shell=True) != 0:
print("Could not deploy to device!")
else: else:
print("Error during build process!") print("Error building project!")
sys.exit(-1) return False
else:
print("Error building project!")
sys.exit(-1)
# Copy apk to bin folder # Copy apk to bin folder
os.makedirs("../bin", exist_ok=True) os.makedirs("../bin", exist_ok=True)
shutil.move('%s.apk' % APK_NAME, "../bin/%s.apk" % APK_NAME) shutil.move('%s.apk' % APK_NAME, "../bin/%s.apk" % APK_NAME)
os.chdir(old_cwd)
return True
class ReadableDir(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
if not os.path.isdir(values):
raise argparse.ArgumentTypeError("{0} is not a valid path".format(values))
if not os.access(values, os.R_OK):
raise argparse.ArgumentTypeError("{0} is not a readable dir".format(values))
setattr(namespace, self.dest,values)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Build and deploy a single example")
parser.add_argument('-deploy', default=False, action='store_true', help="install example on device")
parser.add_argument('-validation', default=False, action='store_true')
parser.add_argument('project_folder', action=ReadableDir)
try:
args = parser.parse_args()
except SystemExit:
sys.exit(1)
ok = main(args.project_folder, args.deploy, args.validation)
sys.exit(0 if ok else 1)

View file

@ -1,13 +0,0 @@
# Install all examples to connected device(s)
import subprocess
import sys
answer = input("Install all vulkan examples to attached device, this may take some time! (Y/N)").lower() == 'y'
if answer:
BUILD_ARGUMENTS = ""
for arg in sys.argv[1:]:
if arg == "-validation":
BUILD_ARGUMENTS += "-validation"
if subprocess.call(("python build-all.py -deploy %s" % BUILD_ARGUMENTS).split(' ')) != 0:
print("Error: Not all examples may have been installed!")
sys.exit(-1)

2
android/uninstall-all.py Normal file → Executable file
View file

@ -1,3 +1,5 @@
#!/usr/bin/env python3
# Remove all examples from connected device(s) # Remove all examples from connected device(s)
import subprocess import subprocess
import sys import sys

2
download_assets.py Normal file → Executable file
View file

@ -1,3 +1,5 @@
#!/usr/bin/env python3
import sys import sys
from urllib.request import urlretrieve from urllib.request import urlretrieve
from zipfile import ZipFile from zipfile import ZipFile