Update make.py to 0.8 (#4579)

This commit is contained in:
Glowbal 2016-10-25 20:11:39 +02:00
parent eeb482eb7e
commit d2f485329f
4 changed files with 189 additions and 127 deletions

View File

@ -3,10 +3,7 @@
#define MAINPREFIX z #define MAINPREFIX z
#define PREFIX ace #define PREFIX ace
#define MAJOR 3 #include "script_version.hpp"
#define MINOR 8
#define PATCHLVL 1
#define BUILD 10
#define VERSION MAJOR.MINOR.PATCHLVL.BUILD #define VERSION MAJOR.MINOR.PATCHLVL.BUILD
#define VERSION_AR MAJOR,MINOR,PATCHLVL,BUILD #define VERSION_AR MAJOR,MINOR,PATCHLVL,BUILD

View File

@ -0,0 +1,4 @@
#define MAJOR 3
#define MINOR 8
#define PATCHLVL 0
#define BUILD 10

View File

@ -16,9 +16,10 @@ This page describes how you can setup your development environment for ACE3, all
- A proper installation of the Arma 3 Tools (available on Steam) - A proper installation of the Arma 3 Tools (available on Steam)
- A properly setup P-drive - A properly setup P-drive
- Run Arma 3 and Arma 3 Tools directly from steam once to install registry entries (and again after every update) - Run Arma 3 and Arma 3 Tools directly from steam once to install registry entries (and again after every update)
- Python 3.x, available [here](http://www.python.org) - [Python 3.x](https://www.python.org/)
- The following Mikero's Tools (available [here](https://dev.withsix.com/projects/mikero-pbodll/files)): DePbo, DeRap, DeOgg, Rapify, MakePbo, pboProject - [Mikero Tools](https://dev.withsix.com/projects/mikero-pbodll/files): DePbo, DeRap, DeOgg, Rapify, MakePbo, PboProject
- A properly setup PATH variable (containing Python, the Mikero's Tools and git) - Python, Mikero Tools and Git in PATH environment variable
- [CBA](https://github.com/CBATeam/CBA_A3/releases/latest) mod (release or development version)
## 2. Why so complicated? ## 2. Why so complicated?
@ -28,24 +29,29 @@ If you have contributed to AGM you might be used to an easier build process, whe
Not offering .exes for the Python scripts we use allows us to make easy changes without the hassle of compiling self-extracting exes all the time. Not offering .exes for the Python scripts we use allows us to make easy changes without the hassle of compiling self-extracting exes all the time.
## 3. Getting ACE ## 3. Getting Source Code
To actually get the ACE source code on your machine, it is recommended that you use Git. Tutorials for this are all around the web, and it allows you to track your changes and easily update your local copy. To actually get the ACE3 source code on your machine, it is recommended that you use Git. Tutorials for this are all around the web, and it allows you to track your changes and easily update your local copy.
If you just want to create a quick and dirty build, you can also directly download the source code using the "Download ZIP" button on the front page of the GitHub repo. If you just want to create a quick and dirty build, you can also directly download the source code using the "Download ZIP" button on the front page of the GitHub repo.
## 4. Initial Setup ## 4. Setup and Building
After ensuring that you have installed all requirements, execute the `setup.py` script found in the `tools` folder. This will do most of the heavy lifting for you, create the links you need and copy the required CBA code to the proper place. Please note that these links are tied to the location of your ACE3 source code, so make sure that the project folder is where you want it to be. We recommend that you store the ACE3 project on your P-drive. ### 4.1 Initial Setup
### 4.1 Manual Setup Navigate to `tools` folder in command line.
Should the script fail, here is how you create the required links manually: ```
cd "[location of the ACE3 project]\tools"
```
First, to set up the links, create `z` folders both in your Arma 3 directory and on your P-drive. Then run the following commands as admin, replacing the text in brackets with the appropriate paths: Execute `setup.py` to create symbolic links to P-drive and Arma 3 directory required for building.
```sh
Should the script fail, you can create the required links manually. First, create `z` folders both in your Arma 3 directory and on your P-drive. Then run the following commands as admin, replacing the text in brackets with the appropriate paths:
```bat
mklink /J "[Arma 3 installation folder]\z\ace" "[location of the ACE3 project]" mklink /J "[Arma 3 installation folder]\z\ace" "[location of the ACE3 project]"
mklink /J "P:\z\ace" "[location of the ACE3 project]" mklink /J "P:\z\ace" "[location of the ACE3 project]"
``` ```
@ -53,33 +59,50 @@ mklink /J "P:\z\ace" "[location of the ACE3 project]"
Then, copy the `cba` folder from the `tools` folder to `P:\x\cba`. Create the `x` folder if needed. That folder contains the parts of the CBA source code that are required for the macros to work. Then, copy the `cba` folder from the `tools` folder to `P:\x\cba`. Create the `x` folder if needed. That folder contains the parts of the CBA source code that are required for the macros to work.
## 5. Creating a Test Build ## 4.2 Creating a Test Build
To create a development build of ACE to test changes or to debug something, run the `build.py` file in the `tools` folder. This will populate the `addons` folder with binarized PBOs. These PBOs still point to the source files in their respective folders however, which allows you to use [file patching](#file-patching). To create a development build of ACE3 to test changes or to debug something, run the `build.py` file in the `tools` folder. This will populate the `addons` folder with binarized PBOs. These PBOs still point to the source files in their respective folders however, which allows you to use [file patching](#file-patching). This also means that you cannot distribute this build to others.
This also means that you cannot distribute this build to others.
To start the game using this build, you can use the following modline: To start the game using this build, you can use the following modline:
```sh ```sh
-mod=@cba_a3;z\ace -mod=@CBA_A3;z\ace
``` ```
## 4.3 Creating a Release Build
## 6. Creating a Release Build To create a complete build of ACE3 that you can use without the source files you will need to:
- Ensure `.hpp` is **NOT** in pboProject's "Exclude From Pbo" list
To create a complete build of ACE that you can use without the source files, run the `make.py` file in the `tools` folder. This will populate the `release` folder with binarized PBOs that you can redistribute. These handle like those of any other mod. When the requirements are met:
- Execute `make.py version increment_build <other-increment-args> force check_external release` in the `tools` folder, replacing `<other-increment-args>` with the part of version you want to increment (options described below)
This will populate the `release` folder with binarized PBOs, compiled extensions, copied extras, bisigns and a bikey. Additionally, an archive file will also be created in the folder. The folder and archive handle like those of any other mod.
Different `make.py` command line options include:
- `version` - update version number in all files and leave them in working directory (leaving this out will still update the version in all files present in the `release` folder, but they will be reverted to not disturb the working directory)
- `increment_build` - increments _build_ version number
- `increment_patch` - increments _patch_ version number (ignored with `increment_minor` or `increment_major`)
- `increment_minor` - increments _minor_ version number and resets _patch_ version number to `0` (ignored with `increment_major`)
- `increment_major` - increments _major_ version number and resets _minor_ and _patch_ version numbers to `0`
- `force` - force rebuild all PBOs, even those already present in the `release` directory (combined with `compile` it will also rebuild all extensions)
- `check_external` - check external references (incompatible only with `<component1> <component2>` and `force <component1> <component2>`)
- `release` - create release packages/archives
- `<component1> <component2>` - build only specified component(s) (incompatible with `release`)
- `force <component1> <component2>` - force rebuild specified component(s) (incompatible with `release`)
## 7. File Patching ## 7. File Patching
File Patching allows you to change the files in an addon while the game is running, requiring only a restart of the mission. This makes it great for debugging, as it cuts down the time required between tests. Note that this only works with PBOs created using MakePBO, as outlined in [Creating a Test Build](#creating-a-test-build). File Patching allows you to change the files in an addon while the game is running, requiring only a restart of the mission. This makes it great for debugging, as it cuts down the time required between tests. Note that this only works with PBOs created using MakePBO, which `build.py` uses.
To run Arma 3 with file patching add the `-filePatching` startup parameter (since Arma 3 v1.50, file patching is disabled by default).
### 7.1 Disabling CBA Function Caching ### 7.1 Disabling CBA Function Caching
By default CBA caches a compiled version of functions to reduce mission load times. This interferes with file patching. There are three ways to disable function caching: By default CBA caches a compiled version of functions to reduce mission load times. This interferes with file patching. There are three ways to disable function caching:
- Load `cba_cache_disable.pbo` (included in CBA's optional folder) - Load `cba_cache_disable.pbo` (included in CBA's optional folder - simply move it to `addons` folder for the time being)
- Add the following to your test missions description.ext: - Add the following to your test missions description.ext:
```cpp ```cpp
@ -94,20 +117,16 @@ class CfgSettings {
}; };
``` ```
- To only disable caching for a single module, hence greatly improving mission restart time, add the following line to the `script_component.hpp` file of said module: - To only disable caching for a single module, hence greatly improving mission restart time, add the following line to the `script_component.hpp` file of said module (prepared in each ACE3 component, simply uncomment):
```cpp ```cpp
#define DISABLE_COMPILE_CACHE #define DISABLE_COMPILE_CACHE
``` ```
### 7.2 Running Arma 3 with File Patching Enabled ### 7.2 Restrictions
Starting from version 1.50, Arma 3 was changed to disable file patching by default, making the feature opt-in. To execute the game with file patching enabled add the `-filePatching` flag to the command line.
### 7.3 Restrictions of File Patching
Files must exist in the built PBOs for file patching to work. If you create a new file you must rebuild the PBO or Arma will not find it in your file paths. Files must exist in the built PBOs for file patching to work. If you create a new file you must rebuild the PBO or Arma will not find it in your file paths.
Configs are not patched during run time, only at load time. You do not have have to rebuild a PBO to make config changes, just restart Arma. You can get around this though if you are on the dev branch of Arma 3 and running the [diagnostic exe](https://community.bistudio.com/wiki/Arma_3_Diagnostics_Exe). That includes `diag_mergeConfigFile` which takes a full system path (as in `diag_mergeConfigFile ["p:\z\ace\addons\my_module\config.cpp"]`) and allows you selectively reload config files. Configs are not patched during run time, only at load time. You do not have have to rebuild a PBO to make config changes, just restart Arma. You can get around this though if you are on the dev branch of Arma 3 and running the [diagnostic exe](https://community.bistudio.com/wiki/Arma_3_Diagnostics_Exe). That includes `diag_mergeConfigFile` which takes a full system path (as in `diag_mergeConfigFile ["p:\z\ace\addons\my_module\config.cpp"]`) and allows you selectively reload config files.
If you need to add/remove files, then you'll need to run build.py again without the game running, and restart. That is all that is required to add new files to then further use in testing. If you need to add/remove files, then you'll need to run `build.py` again without the game running, and restart. That is all that is required to add new files for further use in testing.

View File

@ -30,7 +30,7 @@
############################################################################### ###############################################################################
__version__ = "0.7" __version__ = "0.8"
import sys import sys
@ -51,8 +51,7 @@ import traceback
import time import time
import timeit import timeit
import re import re
import fileinput
from tempfile import mkstemp
if sys.platform == "win32": if sys.platform == "win32":
import winreg import winreg
@ -329,7 +328,6 @@ def print_yellow(msg):
def copy_important_files(source_dir,destination_dir): def copy_important_files(source_dir,destination_dir):
originalDir = os.getcwd() originalDir = os.getcwd()
# Copy importantFiles # Copy importantFiles
@ -340,17 +338,17 @@ def copy_important_files(source_dir,destination_dir):
for file in importantFiles: for file in importantFiles:
filePath = os.path.join(module_root_parent, file) filePath = os.path.join(module_root_parent, file)
# Take only file name for destination path (to put it into root of release dir) if os.path.exists(filePath):
if "\\" in file: print_green("Copying file => {}".format(filePath))
count = file.count("\\") shutil.copy(os.path.join(source_dir,filePath), destination_dir)
file = file.split("\\", count)[-1] else:
print_green("Copying file => {}".format(os.path.join(source_dir,file))) missingFiles.append("{}".format(filePath))
shutil.copyfile(os.path.join(source_dir,filePath),os.path.join(destination_dir,file)) print_error("Failed copying file => {}".format(filePath))
except: except:
print_error("COPYING IMPORTANT FILES.") print_error("COPYING IMPORTANT FILES.")
raise raise
#copy all extension dlls # Copy all extension DLL's
try: try:
os.chdir(os.path.join(source_dir)) os.chdir(os.path.join(source_dir))
print_blue("\nSearching for DLLs in {}".format(os.getcwd())) print_blue("\nSearching for DLLs in {}".format(os.getcwd()))
@ -370,13 +368,13 @@ def copy_important_files(source_dir,destination_dir):
os.chdir(originalDir) os.chdir(originalDir)
def copy_optionals_for_building(mod,pbos): def copy_optionals_for_building(mod,pbos):
src_directories = os.listdir(optionals_root) src_directories = os.listdir(optionals_root)
current_dir = os.getcwd() current_dir = os.getcwd()
print_blue("\nChecking Optionals folder...") print_blue("\nChecking optionals folder...")
try: try:
#special server.pbo processing #special server.pbo processing
files = glob.glob(os.path.join(release_dir, project, "optionals", "*.pbo")) files = glob.glob(os.path.join(release_dir, project, "optionals", "*.pbo"))
for file in files: for file in files:
@ -400,7 +398,6 @@ def copy_optionals_for_building(mod,pbos):
finally: finally:
os.chdir(current_dir) os.chdir(current_dir)
print("")
try: try:
for dir_name in src_directories: for dir_name in src_directories:
mod.append(dir_name) mod.append(dir_name)
@ -467,9 +464,10 @@ def cleanup_optionals(mod):
def purge(dir, pattern, friendlyPattern="files"): def purge(dir, pattern, friendlyPattern="files"):
print_green("Deleting {} files from directory: {}".format(friendlyPattern,dir)) print_green("Deleting {} files from directory: {}".format(friendlyPattern,dir))
for f in os.listdir(dir): if os.path.exists(dir):
if re.search(pattern, f): for f in os.listdir(dir):
os.remove(os.path.join(dir, f)) if re.search(pattern, f):
os.remove(os.path.join(dir, f))
def build_signature_file(file_name): def build_signature_file(file_name):
@ -525,13 +523,13 @@ def addon_restore(modulePath):
return True return True
def get_project_version(): def get_project_version(version_increments=[]):
global project_version global project_version
versionStamp = project_version versionStamp = project_version
#do the magic based on https://github.com/acemod/ACE3/issues/806#issuecomment-95639048 #do the magic based on https://github.com/acemod/ACE3/issues/806#issuecomment-95639048
try: try:
scriptModPath = os.path.join(work_drive, prefix, "main\script_mod.hpp") scriptModPath = os.path.join(module_root, "main\script_version.hpp")
if os.path.isfile(scriptModPath): if os.path.isfile(scriptModPath):
f = open(scriptModPath, "r") f = open(scriptModPath, "r")
@ -541,11 +539,36 @@ def get_project_version():
if hpptext: if hpptext:
majorText = re.search(r"#define MAJOR (.*\b)", hpptext).group(1) majorText = re.search(r"#define MAJOR (.*\b)", hpptext).group(1)
minorText = re.search(r"#define MINOR (.*\b)", hpptext).group(1) minorText = re.search(r"#define MINOR (.*\b)", hpptext).group(1)
patchlvlText = re.search(r"#define PATCHLVL (.*\b)", hpptext).group(1) patchText = re.search(r"#define PATCHLVL (.*\b)", hpptext).group(1)
buildText = re.search(r"#define BUILD (.*\b)", hpptext).group(1) buildText = re.search(r"#define BUILD (.*\b)", hpptext).group(1)
# Increment version (reset all below except build)
if version_increments != []:
if "major" in version_increments:
majorText = int(majorText) + 1
minorText = 0
patchText = 0
elif "minor" in version_increments:
minorText = int(minorText) + 1
patchText = 0
elif "patch" in version_increments:
patchText = int(patchText) + 1
# Always increment build
if "build" in version_increments:
buildText = int(buildText) + 1
print_green("Incrementing version to {}.{}.{}.{}".format(majorText,minorText,patchText,buildText))
with open(scriptModPath, "w", newline="\n") as file:
file.writelines([
"#define MAJOR {}\n".format(majorText),
"#define MINOR {}\n".format(minorText),
"#define PATCHLVL {}\n".format(patchText),
"#define BUILD {}\n".format(buildText)
])
if majorText: if majorText:
versionStamp = "{major}.{minor}.{patchlvl}.{build}".format(major=majorText,minor=minorText,patchlvl=patchlvlText,build=buildText) versionStamp = "{}.{}.{}.{}".format(majorText,minorText,patchText,buildText)
else: else:
print_error("A Critical file seems to be missing or inaccessible: {}".format(scriptModPath)) print_error("A Critical file seems to be missing or inaccessible: {}".format(scriptModPath))
@ -565,22 +588,15 @@ def get_project_version():
def replace_file(filePath, oldSubstring, newSubstring): def replace_file(filePath, oldSubstring, newSubstring):
#Create temp file for line in fileinput.input(filePath, inplace=True):
fh, absPath = mkstemp() # Use stdout directly, print() adds newlines automatically
with open(absPath,'w') as newFile: sys.stdout.write(line.replace(oldSubstring,newSubstring))
with open(filePath) as oldFile:
for line in oldFile:
newFile.write(line.replace(oldSubstring, newSubstring))
newFile.close()
#Remove original file
os.remove(filePath)
#Move new file
shutil.move(absPath, filePath)
def set_version_in_files(): def set_version_in_files():
newVersion = project_version # MAJOR.MINOR.PATCH.BUILD newVersion = project_version # MAJOR.MINOR.PATCH.BUILD
newVersionShort = newVersion[:-2] # MAJOR.MINOR.PATCH newVersionArr = newVersion.split(".")
newVersionShort = ".".join((newVersionArr[0],newVersionArr[1],newVersionArr[2])) # MAJOR.MINOR.PATCH
# Regex patterns # Regex patterns
pattern = re.compile(r"([\d]+\.[\d]+\.[\d]+\.[\d]+)") # MAJOR.MINOR.PATCH.BUILD pattern = re.compile(r"([\d]+\.[\d]+\.[\d]+\.[\d]+)") # MAJOR.MINOR.PATCH.BUILD
@ -598,28 +614,26 @@ def set_version_in_files():
f.close() f.close()
if fileText: if fileText:
# Search and save version stamp, search short if long not found # Version string files
versionFound = re.findall(pattern, fileText) # Search and save version stamp
if not versionFound: versionsFound = re.findall(pattern, fileText) + re.findall(patternShort, fileText)
versionFound = re.findall(patternShort, fileText) # Filter out sub-versions of other versions
versionsFound = [j for i, j in enumerate(versionsFound) if all(j not in k for k in versionsFound[i + 1:])]
# Replace version stamp if any of the new version parts is higher than the one found # Replace version stamp if any of the new version parts is higher than the one found
if versionFound: for versionFound in versionsFound:
# First item in the list findall returns if versionFound:
versionFound = versionFound[0] # Use the same version length as the one found
newVersionUsed = "" # In case undefined
newVersionUsed = "" if versionFound.count(".") == newVersion.count("."):
# Use the same version length as the one found newVersionUsed = newVersion
if len(versionFound) == len(newVersion): if versionFound.count(".") == newVersionShort.count("."):
newVersionUsed = newVersion newVersionUsed = newVersionShort
if len(versionFound) == len(newVersionShort):
newVersionUsed = newVersionShort
# Print change and modify the file if changed
if versionFound != newVersionUsed:
print_green("Changing version {} => {} in {}".format(versionFound, newVersionUsed, filePath))
replace_file(filePath, versionFound, newVersionUsed)
# Print change and modify the file if changed
if newVersionUsed and versionFound != newVersionUsed:
print_green("Changing version {} => {} in {}".format(versionFound, newVersionUsed, filePath))
replace_file(filePath, versionFound, newVersionUsed)
except WindowsError as e: except WindowsError as e:
# Temporary file is still "in use" by Python, pass this exception # Temporary file is still "in use" by Python, pass this exception
pass pass
@ -634,13 +648,17 @@ def stash_version_files_for_building():
try: try:
for file in versionFiles: for file in versionFiles:
filePath = os.path.join(module_root_parent, file) filePath = os.path.join(module_root_parent, file)
# Take only file name for stash location if in subfolder (otherwise it gets removed when removing folders from release dir) if os.path.exists(filePath):
if "\\" in file: # Take only file name for stash location if in subfolder (otherwise it gets removed when removing folders from release dir)
count = file.count("\\") if "\\" in file:
file = file.split("\\", count)[-1] count = file.count("\\")
stashPath = os.path.join(release_dir, file) file = file.split("\\", count)[-1]
print("Temporarily stashing {} => {}.bak for version update".format(filePath, stashPath)) stashPath = os.path.join(release_dir, file)
shutil.copy(filePath, "{}.bak".format(stashPath)) print("Temporarily stashing {} => {}.bak for version update".format(filePath, stashPath))
shutil.copy(filePath, "{}.bak".format(stashPath))
else:
print_error("Failed temporarily stashing {} for version update".format(filePath))
missingFiles.append("{}".format(filePath))
except: except:
print_error("Stashing version files failed") print_error("Stashing version files failed")
raise raise
@ -652,6 +670,8 @@ def stash_version_files_for_building():
def restore_version_files(): def restore_version_files():
try: try:
print_blue("\nRestoring version files...")
for file in versionFiles: for file in versionFiles:
filePath = os.path.join(module_root_parent, file) filePath = os.path.join(module_root_parent, file)
# Take only file name for stash path if in subfolder (otherwise it gets removed when removing folders from release dir) # Take only file name for stash path if in subfolder (otherwise it gets removed when removing folders from release dir)
@ -659,8 +679,9 @@ def restore_version_files():
count = file.count("\\") count = file.count("\\")
file = file.split("\\", count)[-1] file = file.split("\\", count)[-1]
stashPath = os.path.join(release_dir, file) stashPath = os.path.join(release_dir, file)
print("Restoring {}".format(filePath)) if os.path.exists(filePath):
shutil.move("{}.bak".format(stashPath), filePath) print("Restoring {}".format(filePath))
shutil.move("{}.bak".format(stashPath), filePath)
except: except:
print_error("Restoring version files failed") print_error("Restoring version files failed")
raise raise
@ -669,9 +690,9 @@ def restore_version_files():
def get_private_keyname(commitID,module="main"): def get_private_keyname(commitID,module="main"):
global pbo_name_prefix global pbo_name_prefix
global project_version
aceVersion = get_project_version() keyName = str("{prefix}{version}-{commit_id}".format(prefix=pbo_name_prefix,version=project_version,commit_id=commitID))
keyName = str("{prefix}{version}-{commit_id}".format(prefix=pbo_name_prefix,version=aceVersion,commit_id=commitID))
return keyName return keyName
@ -786,6 +807,7 @@ def main(argv):
global prefix global prefix
global pbo_name_prefix global pbo_name_prefix
global ciBuild global ciBuild
global missingFiles
if sys.platform != "win32": if sys.platform != "win32":
print_error("Non-Windows platform (Cygwin?). Please re-run from cmd.") print_error("Non-Windows platform (Cygwin?). Please re-run from cmd.")
@ -854,12 +876,9 @@ See the make.cfg file for additional build options.
if "release" in argv: if "release" in argv:
make_release_zip = True make_release_zip = True
release_version = argv[argv.index("release") + 1]
argv.remove(release_version)
argv.remove("release") argv.remove("release")
else: else:
make_release_zip = False make_release_zip = False
release_version = project_version
if "target" in argv: if "target" in argv:
make_target = argv[argv.index("target") + 1] make_target = argv[argv.index("target") + 1]
@ -889,8 +908,22 @@ See the make.cfg file for additional build options.
else: else:
version_update = False version_update = False
if "--ci" in argv: version_increments = []
argv.remove("--ci") if "increment_build" in argv:
argv.remove("increment_build")
version_increments.append("build")
if "increment_patch" in argv:
argv.remove("increment_patch")
version_increments.append("patch")
if "increment_minor" in argv:
argv.remove("increment_minor")
version_increments.append("minor")
if "increment_major" in argv:
argv.remove("increment_major")
version_increments.append("major")
if "ci" in argv:
argv.remove("ci")
ciBuild = True ciBuild = True
print_yellow("\nCheck external references is set to {}".format(str(check_external))) print_yellow("\nCheck external references is set to {}".format(str(check_external)))
@ -956,21 +989,21 @@ See the make.cfg file for additional build options.
optionals_root = os.path.join(module_root_parent, "optionals") optionals_root = os.path.join(module_root_parent, "optionals")
extensions_root = os.path.join(module_root_parent, "extensions") extensions_root = os.path.join(module_root_parent, "extensions")
commit_id = get_commit_ID()
key_name = versionStamp = get_private_keyname(commit_id)
print_green ("module_root: {}".format(module_root))
if (os.path.isdir(module_root)): if (os.path.isdir(module_root)):
os.chdir(module_root) os.chdir(module_root)
else: else:
print_error ("Directory {} does not exist.".format(module_root)) print_error ("Directory {} does not exist.".format(module_root))
sys.exit() sys.exit(1)
commit_id = get_commit_ID()
get_project_version(version_increments)
key_name = versionStamp = get_private_keyname(commit_id)
print_green ("module_root: {}".format(module_root))
if (os.path.isdir(optionals_root)): if (os.path.isdir(optionals_root)):
print_green ("optionals_root: {}".format(optionals_root)) print_green ("optionals_root: {}".format(optionals_root))
else: else:
print_error ("Directory {} does not exist.".format(optionals_root)) print("optionals_root does not exist: {}".format(optionals_root))
sys.exit()
print_green ("release_dir: {}".format(release_dir)) print_green ("release_dir: {}".format(release_dir))
@ -1022,7 +1055,6 @@ See the make.cfg file for additional build options.
cache = {} cache = {}
# Check the build version (from main) with cached version - forces a full rebuild when version changes # Check the build version (from main) with cached version - forces a full rebuild when version changes
project_version = get_project_version()
cacheVersion = "None"; cacheVersion = "None";
if 'cacheVersion' in cache: if 'cacheVersion' in cache:
cacheVersion = cache['cacheVersion'] cacheVersion = cache['cacheVersion']
@ -1046,6 +1078,9 @@ See the make.cfg file for additional build options.
print_error("Cannot create release directory") print_error("Cannot create release directory")
raise raise
failedBuilds = []
missingFiles = []
# Update version stamp in all files that contain it # Update version stamp in all files that contain it
# Update version only for release if full update not requested (backup and restore files) # Update version only for release if full update not requested (backup and restore files)
print_blue("\nChecking for obsolete version numbers...") print_blue("\nChecking for obsolete version numbers...")
@ -1056,14 +1091,12 @@ See the make.cfg file for additional build options.
set_version_in_files(); set_version_in_files();
print("Version in files has been changed, make sure you commit and push the updates!") print("Version in files has been changed, make sure you commit and push the updates!")
amountOfBuildsFailed = 0
namesOfBuildsFailed = []
try: try:
# Temporarily copy optionals_root for building. They will be removed later. # Temporarily copy optionals_root for building. They will be removed later.
optionals_modules = [] if (os.path.isdir(optionals_root)):
optional_files = [] optionals_modules = []
copy_optionals_for_building(optionals_modules,optional_files) optional_files = []
copy_optionals_for_building(optionals_modules,optional_files)
# Get list of subdirs in make root. # Get list of subdirs in make root.
dirs = next(os.walk(module_root))[1] dirs = next(os.walk(module_root))[1]
@ -1128,15 +1161,14 @@ See the make.cfg file for additional build options.
if (file.endswith(".pbo") and os.path.isfile(os.path.join(obsolete_check_path,file))): if (file.endswith(".pbo") and os.path.isfile(os.path.join(obsolete_check_path,file))):
if check_for_obsolete_pbos(module_root, file): if check_for_obsolete_pbos(module_root, file):
fileName = os.path.splitext(file)[0] fileName = os.path.splitext(file)[0]
print_yellow("Removing obsolete file => {}".format(file)) print_yellow("Removing obsolete pbo => {}".format(file))
purge(obsolete_check_path, "{}\..".format(fileName), "{}.*".format(fileName)) purge(obsolete_check_path, "{}\..".format(fileName), "{}.*".format(fileName))
obsolete_check_path = os.path.join(module_root, release_dir, project) obsolete_check_path = os.path.join(module_root, release_dir, project)
for file in os.listdir(obsolete_check_path): for file in os.listdir(obsolete_check_path):
if (file.endswith(".dll") and os.path.isfile(os.path.join(obsolete_check_path,file))): if (file.endswith(".dll") and os.path.isfile(os.path.join(obsolete_check_path,file))):
if check_for_obsolete_pbos(extensions_root, file): if not os.path.exists(os.path.join(module_root_parent, file)):
fileName = os.path.splitext(file)[0] print_yellow("Removing obsolete dll => {}".format(file))
print_yellow("Removing obsolete file => {}".format(file))
try: try:
os.remove(os.path.join(obsolete_check_path,file)) os.remove(os.path.join(obsolete_check_path,file))
except: except:
@ -1282,8 +1314,7 @@ See the make.cfg file for additional build options.
print_error("pboProject return code == {}".format(str(ret))) print_error("pboProject return code == {}".format(str(ret)))
print_error("Module not successfully built/signed. Check your {}temp\{}_packing.log for more info.".format(work_drive,module)) print_error("Module not successfully built/signed. Check your {}temp\{}_packing.log for more info.".format(work_drive,module))
print ("Resuming build...") print ("Resuming build...")
amountOfBuildsFailed += 1 failedBuilds.append("{}".format(module))
namesOfBuildsFailed.append("{}".format(module))
continue continue
# Back to the root # Back to the root
@ -1379,7 +1410,8 @@ See the make.cfg file for additional build options.
finally: finally:
copy_important_files(module_root_parent,os.path.join(release_dir, project)) copy_important_files(module_root_parent,os.path.join(release_dir, project))
cleanup_optionals(optionals_modules) if (os.path.isdir(optionals_root)):
cleanup_optionals(optionals_modules)
if not version_update: if not version_update:
restore_version_files() restore_version_files()
@ -1399,8 +1431,7 @@ See the make.cfg file for additional build options.
# Make release # Make release
if make_release_zip: if make_release_zip:
release_name = "{}_{}".format(zipPrefix, release_version) release_name = "{}_{}".format(zipPrefix, project_version)
print_blue("\nMaking release: {}.zip".format(release_name))
try: try:
# Delete all log files # Delete all log files
@ -1415,7 +1446,10 @@ See the make.cfg file for additional build options.
os.remove(os.path.join(release_dir, file)) os.remove(os.path.join(release_dir, file))
# Create a zip with the contents of release folder in it # Create a zip with the contents of release folder in it
print_blue("\nMaking release: {}.zip ...".format(release_name))
print("Packing...")
release_zip = shutil.make_archive("{}".format(release_name), "zip", release_dir) release_zip = shutil.make_archive("{}".format(release_name), "zip", release_dir)
# Move release zip to release folder # Move release zip to release folder
shutil.copy(release_zip, release_dir) shutil.copy(release_zip, release_dir)
os.remove(release_zip) os.remove(release_zip)
@ -1450,15 +1484,23 @@ See the make.cfg file for additional build options.
except: except:
print_error("Could not copy files. Is Arma 3 running?") print_error("Could not copy files. Is Arma 3 running?")
if amountOfBuildsFailed > 0: if len(failedBuilds) > 0 or len(missingFiles) > 0:
print_error("Build failed. {} pbos failed.".format(amountOfBuildsFailed)) if len(failedBuilds) > 0:
print()
print_error("Build failed! {} PBOs failed!".format(len(failedBuilds)))
for failedBuild in failedBuilds:
print("- {} failed.".format(failedBuild))
for failedModuleName in namesOfBuildsFailed: if len(missingFiles) > 0:
print("- {} failed.".format(failedModuleName)) missingFiles = set(missingFiles)
print()
print_error("Missing files! {} files not found!".format(len(missingFiles)))
for missingFile in missingFiles:
print("- {} failed.".format(missingFile))
sys.exit(1) sys.exit(1)
else: else:
print_green("\Completed with 0 errors.") print_green("\nCompleted with 0 errors.")
if __name__ == "__main__": if __name__ == "__main__":
start_time = timeit.default_timer() start_time = timeit.default_timer()