diff --git a/tools/make.cfg b/tools/make.cfg index e89568d60e..5e2d9d78da 100644 --- a/tools/make.cfg +++ b/tools/make.cfg @@ -17,6 +17,11 @@ project = @ace # Default: None # key = D:\Program Files (x86)\Bohemia Interactive\Tools\DSSignFile Tools\keys\ace_preAlpha.biprivatekey +# Path to where private keys are automatically created if the command-line parameter "key" is used +# Make sure this isn't in your public repository! +# Default: \private_keys +# private_key_path = P:\private_keys + # If set to True, the make system will attempt to autodetect addons in the # current folder by looking for directories with 'config.cpp' in them. # Default: True diff --git a/tools/make.py b/tools/make.py index 3b7bf9d94e..597ccb314a 100644 --- a/tools/make.py +++ b/tools/make.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # vim: set fileencoding=utf-8 : # make.py @@ -60,6 +60,9 @@ module_root = "" release_dir = "" module_root_parent = "" optionals_root = "" +key_name = "ace_preAlpha" +key = "" +dssignfile = "" ############################################################################### # http://akiscode.com/articles/sha-1directoryhash.shtml @@ -196,6 +199,7 @@ def find_bi_tools(work_drive): else: raise Exception("BadTools","Arma 3 Tools are not installed correctly or the P: drive needs to be created.") + def find_depbo_tools(regKey): """Use registry entries to find DePBO-based tools.""" stop = False @@ -248,6 +252,7 @@ def find_depbo_tools(regKey): #Strip any quotations from the path due to a MikeRo tool bug which leaves a trailing space in some of its registry paths. return [pboproject_path.strip('"'),rapify_path.strip('"'),makepbo_path.strip('"')] + def color(color): """Set the color. Works on Win32 and normal terminals.""" if sys.platform == "win32": @@ -336,6 +341,7 @@ def copy_important_files(source_dir,destination_dir): finally: os.chdir(originalDir) + def copy_optionals_for_building(mod,pbos): src_directories = os.listdir(optionals_root) current_dir = os.getcwd() @@ -350,10 +356,15 @@ def copy_optionals_for_building(mod,pbos): #print ("Adding the following file: " + file_name) pbos.append(file_name) pbo_path = os.path.join(release_dir, "@ace","optionals",file_name) + sigFile_name = file_name +"."+ key_name + ".bisign" + sig_path = os.path.join(release_dir, "@ace","optionals",sigFile_name) if (os.path.isfile(pbo_path)): print("Moving " + pbo_path + " for processing.") shutil.move(pbo_path, os.path.join(release_dir,"@ace","addons",file_name)) + if (os.path.isfile(sig_path)): + #print("Moving " + sig_path + " for processing.") + shutil.move(sig_path, os.path.join(release_dir,"@ace","addons",sigFile_name)) except: print_error("Error in moving") raise @@ -384,7 +395,8 @@ def copy_optionals_for_building(mod,pbos): finally: os.chdir(current_dir) -def cleanup_optionals(mod,pbos): + +def cleanup_optionals(mod): print("") try: for dir_name in mod: @@ -400,9 +412,17 @@ def cleanup_optionals(mod,pbos): file_name = "ace_{}.pbo".format(dir_name) src_file_path = os.path.join(release_dir, "@ace","addons",file_name) dst_file_path = os.path.join(release_dir, "@ace","optionals",file_name) + + sigFile_name = file_name +"."+ key_name + ".bisign" + src_sig_path = os.path.join(release_dir, "@ace","addons",sigFile_name) + dst_sig_path = os.path.join(release_dir, "@ace","optionals",sigFile_name) + if (os.path.isfile(src_file_path)): #print("Preserving " + file_name) os.renames(src_file_path,dst_file_path) + if (os.path.isfile(src_sig_path)): + #print("Preserving " + sigFile_name) + os.renames(src_sig_path,dst_sig_path) except FileExistsError: print_error(file_name + " already exists") continue @@ -411,12 +431,40 @@ def cleanup_optionals(mod,pbos): except: print_error("Cleaning Optionals Failed") raise + + +def purge(dir, pattern, friendlyPattern="files"): + print_green("Deleting " + friendlyPattern + " files from directory: " + dir) + for f in os.listdir(dir): + if re.search(pattern, f): + os.remove(os.path.join(dir, f)) + + +def build_signature_file(file_name): + global key + global dssignfile + print("Signing with " + key + ".") + ret = subprocess.call([dssignfile, key, file_name]) + if ret == 0: + return True + else: + return False ############################################################################### + def main(argv): """Build an Arma addon suite in a directory from rules in a make.cfg file.""" print_blue(("\nmake.py for Arma, modified for Advanced Combat Environment v" + __version__)) + global work_drive + global module_root + global release_dir + global module_root_parent + global optionals_root + global key_name + global key + global dssignfile + if sys.platform != "win32": print_error("Non-Windows platform (Cygwin?). Please re-run from cmd.") sys.exit(1) @@ -436,7 +484,7 @@ def main(argv): release_version = 0 # Version of release use_pboproject = True # Default to pboProject build tool make_target = "DEFAULT" # Which section in make.cfg to use for the build - new_key = False # Make a new key and use it to sign? + new_key = True # Make a new key and use it to sign? quiet = False # Suppress output from build tool? # Parse arguments @@ -512,6 +560,8 @@ See the make.cfg file for additional build options. else: check_external = False + print_yellow("\nCheck external references is set to " + str(check_external)) + # Get the directory the make script is in. make_root = os.path.dirname(os.path.realpath(__file__)) make_root_parent = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) @@ -524,26 +574,27 @@ See the make.cfg file for additional build options. commit_id = subprocess.check_output(["git", "rev-parse", "HEAD"]) commit_id = str(commit_id, "utf-8")[:8] + key_name = str(key_name+"-"+commit_id) except: print_error("FAILED TO DETERMINE COMMIT ID.") commit_id = "NOGIT" cfg = configparser.ConfigParser(); try: - global work_drive - global module_root - global release_dir - global module_root_parent - global optionals_root - cfg.read(os.path.join(make_root, "make.cfg")) # Project name (with @ symbol) project = cfg.get(make_target, "project", fallback="@"+os.path.basename(os.getcwd())) + # BI Tools work drive on Windows + work_drive = cfg.get(make_target, "work_drive", fallback="P:\\") + # Private key path key = cfg.get(make_target, "key", fallback=None) + # Private key creation directory + private_key_path = cfg.get(make_target, "private_key_path", fallback=os.path.join(work_drive, "private_keys")) + # Project prefix (folder path) prefix = cfg.get(make_target, "prefix", fallback="") @@ -561,9 +612,6 @@ See the make.cfg file for additional build options. # List of directories to ignore when detecting ignore = [x.strip() for x in cfg.get(make_target, "ignore", fallback="release").split(',')] - # BI Tools work drive on Windows - work_drive = cfg.get(make_target, "work_drive", fallback="P:\\") - # Which build tool should we use? build_tool = cfg.get(make_target, "build_tool", fallback="addonbuilder").lower() @@ -640,6 +688,20 @@ See the make.cfg file for additional build options. print ("No cache found.") cache = {} + if not os.path.isdir(os.path.join(release_dir, project, "addons")): + try: + os.makedirs(os.path.join(release_dir, project, "addons")) + except: + print_error("Cannot create release directory") + raise + + if not os.path.isdir(os.path.join(release_dir, project, "keys")): + try: + os.makedirs(os.path.join(release_dir, project, "keys")) + except: + print_error("Cannot create release directory") + raise + #Temporarily copy optionals_root for building. They will be removed later. optionals_modules = [] optional_files = [] @@ -660,31 +722,42 @@ See the make.cfg file for additional build options. # Make the key specified from command line if necessary. if new_key: if not os.path.isfile(os.path.join(module_root, key_name + ".biprivatekey")): - print_green("\nRequested key does not exist.") + print_yellow("\nRequested key does not exist.") + try: + os.makedirs(private_key_path) + except: + pass + curDir = os.getcwd() + os.chdir(private_key_path) ret = subprocess.call([dscreatekey, key_name]) # Created in make_root + os.chdir(curDir) if ret == 0: - print_blue("Created: " + os.path.join(module_root, key_name + ".biprivatekey")) + print_green("Created: " + os.path.join(private_key_path, key_name + ".biprivatekey")) + print("Removing any old signature keys...") + purge(os.path.join(module_root, release_dir, project, "addons"), "^.*\.bisign$","*.bisign") + purge(os.path.join(module_root, release_dir, project, "optionals"), "^.*\.bisign$","*.bisign") + purge(os.path.join(module_root, release_dir, project, "keys"), "^.*\.bikey$","*.bikey") else: print_error("Failed to create key!") try: - print_blue("Copying public key to release directory.") + print("Copying public key to release directory.") try: - os.makedirs(os.path.join(module_root, release_dir, "Keys")) + os.makedirs(os.path.join(module_root, release_dir, project, "keys")) except: pass - shutil.copyfile(os.path.join(module_root, key_name + ".bikey"), os.path.join(module_root, release_dir, "Keys", key_name + ".bikey")) + shutil.copyfile(os.path.join(private_key_path, key_name + ".bikey"), os.path.join(module_root, release_dir, project, "keys", key_name + ".bikey")) except: - raise print_error("Could not copy key to release directory.") + raise else: - print_green("\nNOTE: Using key " + os.path.join(module_root, key_name + ".biprivatekey")) + print_green("\nNOTE: Using key " + os.path.join(private_key_path, key_name + ".biprivatekey")) - key = os.path.join(module_root, key_name + ".biprivatekey") + key = os.path.join(private_key_path, key_name + ".biprivatekey") # For each module, prep files and then build. @@ -692,6 +765,7 @@ See the make.cfg file for additional build options. for module in modules: print_green("\nMaking " + module + "-"*max(1, (60-len(module)))) missing = False + sigMissing = False # Cache check if module in cache: @@ -706,16 +780,23 @@ See the make.cfg file for additional build options. # Hash the module new_sha = get_directory_hash(os.path.join(module_root, module)) - # Is the pbo file missing? + # Is the pbo or sig file missing? missing = not os.path.isfile(os.path.join(release_dir, project, "addons", "ace_{}.pbo".format(module))) + sigFile = pbo_name_prefix+module + ".pbo." + key_name + ".bisign" + sigMissing = not os.path.isfile(os.path.join(release_dir, project, "addons", sigFile )) + if missing: - print("ace_{}.pbo".format(module) + " is missing. Building...") + print_yellow("Missing PBO file ace_{}.pbo".format(module) + ". Building...") # Check if it needs rebuilt # print ("Hash:", new_sha) if old_sha == new_sha and not missing: if not force_build: print("Module has not changed.") + if sigMissing: + if key: + print("Missing Signature key " + sigFile) + build_signature_file(os.path.join(module_root, release_dir, project, "addons", pbo_name_prefix + module + ".pbo")) # Skip everything else continue @@ -740,13 +821,13 @@ See the make.cfg file for additional build options. try: # Remove the old pbo, key, and log - old = os.path.join(module_root, release_dir, project, "Addons", module) + "*" + old = os.path.join(module_root, release_dir, project, "addons", pbo_name_prefix+module) + "*" files = glob.glob(old) for f in files: os.remove(f) if pbo_name_prefix: - old = os.path.join(module_root, release_dir, project, "Addons", pbo_name_prefix+module) + "*" + old = os.path.join(module_root, release_dir, project, "addons", pbo_name_prefix+module) + "*" files = glob.glob(old) for f in files: os.remove(f) @@ -759,11 +840,11 @@ See the make.cfg file for additional build options. # Build the module into a pbo print_blue("Building: " + os.path.join(work_drive, prefix, module)) - print_blue("Destination: " + os.path.join(module_root, release_dir, project, "Addons")) + print_blue("Destination: " + os.path.join(module_root, release_dir, project, "addons")) # Make destination folder (if needed) try: - os.makedirs(os.path.join(module_root, release_dir, project, "Addons")) + os.makedirs(os.path.join(module_root, release_dir, project, "addons")) except: pass @@ -815,7 +896,7 @@ See the make.cfg file for additional build options. if os.path.isfile(os.path.join(work_drive, prefix, module, "$NOBIN$")): print_green("$NOBIN$ Found. Proceeding with non-binarizing!") - cmd = [makepboTool, "-P","-A","-L","-N","-G", os.path.join(work_drive, prefix, module),os.path.join(module_root, release_dir, project,"Addons")] + cmd = [makepboTool, "-P","-A","-L","-N","-G", os.path.join(work_drive, prefix, module),os.path.join(module_root, release_dir, project,"addons")] else: if check_external: @@ -837,7 +918,7 @@ See the make.cfg file for additional build options. # Prettyprefix rename the PBO if requested. if pbo_name_prefix: try: - os.rename(os.path.join(module_root, release_dir, project, "Addons", module+".pbo"), os.path.join(module_root, release_dir, project, "Addons", pbo_name_prefix+module+".pbo")) + os.rename(os.path.join(module_root, release_dir, project, "addons", module+".pbo"), os.path.join(module_root, release_dir, project, "addons", pbo_name_prefix+module+".pbo")) except: raise print_error("Could not rename built PBO with prefix.") @@ -845,9 +926,9 @@ See the make.cfg file for additional build options. if key: print("Signing with " + key + ".") if pbo_name_prefix: - ret = subprocess.call([dssignfile, key, os.path.join(module_root, release_dir, project, "Addons", pbo_name_prefix + module + ".pbo")]) + ret = subprocess.call([dssignfile, key, os.path.join(module_root, release_dir, project, "addons", pbo_name_prefix + module + ".pbo")]) else: - ret = subprocess.call([dssignfile, key, os.path.join(module_root, release_dir, project, "Addons", module + ".pbo")]) + ret = subprocess.call([dssignfile, key, os.path.join(module_root, release_dir, project, "addons", module + ".pbo")]) if ret == 0: build_successful = True @@ -887,7 +968,7 @@ See the make.cfg file for additional build options. # Call AddonBuilder os.chdir("P:\\") - cmd = [addonbuilder, os.path.join(work_drive, prefix, module), os.path.join(make_root, release_dir, project, "Addons"), "-clear", "-project="+work_drive] + cmd = [addonbuilder, os.path.join(work_drive, prefix, module), os.path.join(make_root, release_dir, project, "addons"), "-clear", "-project="+work_drive] if not do_binarize: cmd.append("-packonly") @@ -910,7 +991,7 @@ See the make.cfg file for additional build options. # Prettyprefix rename the PBO if requested. if pbo_name_prefix: try: - os.rename(os.path.join(make_root, release_dir, project, "Addons", module+".pbo"), os.path.join(make_root, release_dir, project, "Addons", pbo_name_prefix+module+".pbo")) + os.rename(os.path.join(make_root, release_dir, project, "addons", module+".pbo"), os.path.join(make_root, release_dir, project, "addons", pbo_name_prefix+module+".pbo")) except: raise print_error("Could not rename built PBO with prefix.") @@ -920,9 +1001,9 @@ See the make.cfg file for additional build options. if key: print("Signing with " + key + ".") if pbo_name_prefix: - ret = subprocess.call([dssignfile, key, os.path.join(make_root, release_dir, project, "Addons", pbo_name_prefix + module + ".pbo")]) + ret = subprocess.call([dssignfile, key, os.path.join(make_root, release_dir, project, "addons", pbo_name_prefix + module + ".pbo")]) else: - ret = subprocess.call([dssignfile, key, os.path.join(make_root, release_dir, project, "Addons", module + ".pbo")]) + ret = subprocess.call([dssignfile, key, os.path.join(make_root, release_dir, project, "addons", module + ".pbo")]) if ret == 0: build_successful = True @@ -965,13 +1046,16 @@ See the make.cfg file for additional build options. print_green("\nDone.") + copy_important_files(module_root_parent,os.path.join(release_dir, "@ace")) + cleanup_optionals(optionals_modules) + # Make release if make_release: print_blue("\nMaking release: " + project + "-" + release_version + ".zip") try: # Delete all log files - for root, dirs, files in os.walk(os.path.join(module_root, release_dir, project, "Addons")): + for root, dirs, files in os.walk(os.path.join(module_root, release_dir, project, "addons")): for currentFile in files: if currentFile.lower().endswith("log"): os.remove(os.path.join(root, currentFile)) @@ -1004,8 +1088,6 @@ See the make.cfg file for additional build options. except: print_error("Could not copy files. Is Arma 3 running?") - copy_important_files(module_root_parent,os.path.join(release_dir, "@ace")) - cleanup_optionals(optionals_modules,optional_files) if __name__ == "__main__": main(sys.argv) input("Press Enter to continue...")