ACE3/tools/make.py
Drofseh 4d1a5194ea Update from acemod master (#1)
* Generic cycle attack profile key for missile guidance (#4787)

* Generic cycle attack profile key for missile guidance

* Add hint for weapons without huds

* Fix Korean strings

* Cleanup

* Frag Superanal Cleanup (#4803)

* Minimal frag cleanup

* Why did this even happen in the first place

Conflicts:
	addons/frag/functions/fnc_frago.sqf

* New Function to Show BIS Style Curator Messages (#4844)

* Add showMessage fnc that shows BIS curator messages

* Add documentation

* Make function simpler

* Modify to fir new syntax

* Handle non-string args

* Generic cycle attack profile key for missile guidance (#4787)

* Generic cycle attack profile key for missile guidance

* Add hint for weapons without huds

* Fix Korean strings

* Cleanup

* Cache Nametags onDraw3D flags. (#4847)

* Cache Nametag flags

* Remove unused privates

* Fix fading, cursorOnly mode, improve cache reset

* Fix header

* Fix Javelin for AI (#4857)

* Optimize Laserpointer (#4859)

* only process one unit every 0.1 seconds

* exitWith to keep indentation low

* use event to determine isIR and isTI

* correctly remove units with turned on lights from their arrays

* handle weaponAccessories command reporting nil

Conflicts:
	addons/laserpointer/XEH_postInit.sqf

* Fix RHS m113 compat (#4881)

* Fix passenger actions - Ensure order of MainActions (#4883)

* Fix getPos order (#4889)

* Use createSimpleObject to create tags (#4892)

* Update CBA required version (#4899)

* prep v3.9.0

* Update required CBA version

* Medical/Repair 3den Toolbox State Fix (Bug fix for #4902) (#4905)

* Update medical/repair 3den away from missionNamespace

* Simplify save

* Hide earplug actions if setting disabled (#4913)

* Hide earplug actions if setting disabled

* Add lines that are new

* Pass map gestures draw by reference (#4914)

* Verify params in atragmx profile gunlist (#4917)

* Fix setVariablePublic reseting to old values (#4938)

* Fix setVariablePublic reseting to old values

* Don't drop delay time

* Reload medical configs into extension on save load (#4941)

* Fix lost magazines when restoring gunbag mags (#4942)

* Add RHS GREF compat for RKG-3 grenade (#4944)

* Fix intergrated scopes (#4945)

* Require CBA v3.2.1 (#4954)

Conflicts:
	addons/main/script_mod.hpp

* Fix gforce avg on switching to aircraft (#4955)

* Fix gforce avg on switching to aircraft

* Dont use for-loop for neutral g-forces array

* Update maps' latitude (#4961)

- add Dingor
 - add MSKE 2017
 - add G.O.S Leskovets
 - add Trung Si
 - add I44: Omaha v2
 - add I44: Neaville and Neaville (Winter)
 - add I44: Battle of the Bulge

* Fix rangecard not showing wind/lead (#4964)

* Give options menu list focus (#4965)

* Cleanup use of ACE_HashLocation (#4975)

* Add validation on group id for map gesture color mapping (#4976)

* Fix vehicle lock lockpick condition, Fix lockpicking from inside (#4985)

* Prep 3.9.1 release

* Fix cookoff desynch, fix #4900

Randomness would be calculated on every machine, but only vehicle explosion and sound are done by the server. Smoke, light and fire sfx are done by each client.

* also fix cooking off ammo boxes

* Fix cookoff desynch, fix #4900

Randomness would be calculated on every machine, but only vehicle explosion and sound are done by the server. Smoke, light and fire sfx are done by each client.

* also fix cooking off ammo boxes

* Add rearm config for Nemmera (#4992)

* Sandbags surfaces update (#4971)

* Use private keyword, move surface blacklist to script_component.hpp

* Check height above terrain

* Add Tanoa surfaces, Check if in water

* Use 'dust' config entry to determine surface, Add common canDig (checks dustyness)

* Re-enable compile cache

* Revert to surface blacklist with dust as fallback

* Move surface blacklist to script_component because SQF validator complains

* Fix unarmed sprint->prone + minor perf improvments (#4887)

* AB : AtragMx wiki updated (#4982)

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Add files via upload

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update publish.py for RHS Gref Compat (#4999)

* Update atragmx.md

* Delete atragmx1.jpg

* Add files via upload

* Translation: Missing 3 strings in Polish (#5008)

* update user url

* Jpn translate to v391 (#5011)

* jpn translate slideshow

Add the Japanese translation for slideshow for v3.9.1

* jpn translate for cookoff

Add the Japanese translation for cookoff of the v3.9.1

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Update atragmx.md

* Prep 3.9.1 release

* Update atragmx.md

* Dupplicate Polish entry (#5018)

https://i.gyazo.com/c7557980d7d4c36709be78ef1f2c2a8f.png

* Update atragmx.md

* Add custom CfgUIGrids for microDagr (#5014)

* Update atragmx.md

* Add files via upload

* Update atragmx.md

* fix links and `alt` texts

* Duplicate French entry (#5032)

Removes a duplicate French entry, didn't saw it :/

* Wiki range card updated (#5029)

* Update rangecard.md

* Update rangecard.md

* Update rangecard.md

* Add files via upload

* Update rangecard.md

* fix links and `alt` texts

* Wiki Advanced Ballistics updated (#5030)

* Update advanced-ballistics.md

* Update advanced-ballistics.md

* Update advanced-ballistics.md

* Update advanced-ballistics.md

* Update advanced-ballistics.md

* Update advanced-ballistics.md

* Update advanced-ballistics.md

* Update advanced-ballistics.md

* Update advanced-ballistics.md

* Update advanced-ballistics.md

* Add files via upload

* Update advanced-ballistics.md

* fix links and `alt` texts

* Wiki Kestrel 4500 updated (#5027)

* Update kestrel4500.md

* Update kestrel4500.md

* Update kestrel4500.md

* Update kestrel4500.md

* Update kestrel4500.md

* Update kestrel4500.md

* Update kestrel4500.md

* fix links and `alt` texts

* Wiki scope updated (#5028)

* Update scopes.md

* Update scopes.md

* Update scopes.md

* Add files via upload

* Update scopes.md

* fix links and `alt` texts

* French loc improvement (#5017)

* French loc improvement

(French) text as it is clutters the interface interface, making it
uneasy for medic to use the it smoothly.
https://i.gyazo.com/8d7a5bb2f15dd1332cec36f2eef510c8.png

Hopefully suggested changes fixes that.

There were a few typos and, in my opinion, unaccurate or missing
translation.
Based on French Army Field Manual, actual paramedical personnel and past
work on XMedSys.

* Pull 5017 with spaces instead of tabs

https://github.com/acemod/ACE3/pull/5017

* Fixes a minor typo

Selectioner -> Sélectionner

* Duplicate entry (French)

Removes a duplicate entry, didn't saw first time

* Wiki scope updated_1 (#5037)

* Update scopes.md

* Update scopes.md

* Update scopes.md

* Add files via upload

* Update scopes.md

* fix links and `alt` texts

* Delete scope_module.jpg

* Add files via upload

* Update scopes.md

* French additions (Cargo) (#5040)

Minor French text additions

* French loc fixes (ballistics) (#5041)

Minor French loc fixes

* Update documentation for Vector21

Add explanation on how to switch range and azimuth units

* Add blank line after heading

* Update vector.md

* Update atragmx.md

* fix link atragmx.md

* Fix dagr not showing bearing in mils (#5047)

* Fix cargo loading to locked vehicle (#5049)

* Update ACEX to 3.2.0.4 (#5062)

* tweak ACE main menu info box

* use https for main menu

* use https for main men

* fix highlighted info box color (#5075)

* Filter headless clients from zeus teleportation (#5070)

* Use setShotParents in Advanced Throwing (#5052)

* Use setShotParents in Advanced Throwing - close #5034

* Execute setShotParents on server (server-only command)

* Fix FCS Vehicle Init (#5063)

* Tweak overheating (#4969)

* Tweak overheating

* Add define for constant magic num

* Virtual units - skip goggles/hearing effects (#4988)

* Disable goggles and hearing fx for virtual units

* Cleanup some other CBA_fnc_addPlayerEventHandler

* Fix tagging model cache (#5055)

* Frag - Cleanup and Performance (#5010)

* Frag - Cleanup and Performance

* Add dots

* (Medical) French text - minor changes (#5043)

Minor changes due to users feedback

* French localization complete (#5079)

* French localization complete

* STR_ACE_MapTools_drawStaightLines_description fix

"l'on doit survoler le milieu du trait pour pouvoir le supprimer"

* Space management

alganthe :
Espace en trop après référence.

* Diapo

alganthe :
Slideshow -> diaporama
Slide -> diapo

* Cohesion fix

alganthe :
Vous avez traduit le nom du module juste au dessus (displayname).
Il est vrai que les créateurs de missions doivent consulter une documentation principalement écrite en anglais, donc vous avez le choix de traduire la ligne ci-dessus ou la garder, les deux semblent valide à mes yeux.

Reasoning : since French mission makers read reference material in English, it makes sense not to translate it and thus keep it simple for them.

* Caisses de munitions

caisse de mun -> caisses de munitions

* STR_ACE_Scopes_Description

Oops, forgot that one.

* Fix RHS mp7 UBC (#5085)

* Fix broken  url on modules wiki page (#5088)

* Fix Javelin for AI for RHS (#5096)

* Remove CfgAiSkill config changes (#5091)

* Remove CfgAiSkill config changes

* Add note to wiki about removal

* Add volume toggle docs (#5077)

* Add volume toggle docs

* Change version and remove ref to another mod

* Changes as per review

* Fix numbers

* Fix refuel nozzle dropping to ground (#5119)

* Fix jerry can refuel interaction overlapping (#5107)

* Add keybind to toggle interaction menu (#5095)

* Add Paradrop Time Coefficient Setting (#5116)

* Add coef

* Show cargo paradrop time in menu

* Unary command, not a function call

* If drop time is 0 don't show a progress bar

* Add the setting to cargo's editor module

* Swap values

* Remove extra comma

* Move unload message to paradrop event

This way the message will show up on all paradrops, not just immidiate
ones.

* Disable search on spectator unit tree

Finally found out how to do this. Having search enabled on the unit tree might seem useful, but it doesn't interact well with keyboard shortcuts used for other purposes like camera control as it causes the tree to jump around unexpectedly.

* Simplify spectator compass code

* Optimize spectator unit list function

Making good use of the new `select` CODE syntax.

* Add 3den info to spectator template

- A `displayName` shown in the 3DEN multiplayer attributes display
- A `respawnTypes` array to define which respawn types the template is compatible with (currently only includes `2,3` - need to investigate compatibility with other types further).

* Fix #5081

The default values of the arrays manipulated by these functions overwrite
any changes applied before settings have finished initalising. This won't
be an issue after #4456 is merged in future.

* Improve Japanese translation (#5133)

* Add the Japanese translation for cargo

Add the Japanese translation for cargo, fix to html tag and few improvement

* fix the Japanese transltion

Fix the Japanese translation for attach. it was used wrong html tag.

* changed The Japanese translation

Changed the Japanese translation of azimuth angle

* fix The Japanese translation

Fix the Japanese translation for chemlights. it was used wrong html tag.

* fix The Japanese translation

Fix the Japanese translation for laserpointer. it was used wrong html tag.

* Fix the Japanese translation

Fix the Japanese translation for medical_blood. it was used wrong translation.

* Fix the Japanese translation

Fix the Japanese translation for rearm. it was used wrong translation.

* add The Japanese translation

add The Japanese translation to new words

* Improve The Japanese translation

Improve, fix and change The Japanese translation

* minor fix to Japanese translation

minor fix to Japanese translation. see b1b07c5

* Fix Spotting Scope Interaction Point (#5132)

* Disable ACE_FCS on RHS Russian Attack Helis (#5145)

* Switch 2 perm PFEH to everyFrame (#5140)

* Explosives - Add code handler for detonation (#5115)

* Explosives - Add code handler for detonation

* Add info to wiki

* Fix cellphone in demo code, move example to wiki

* Zeus Suppression Module (#4977)

* add base structure

* Add getModuleDestination

* Add 2d map support, debug

* Cleanup, handle weapon max range

* Handle non-local units

* Use new showMessage func

* Run on groups when placed on leader

* Support for Indirect Fire Vehicles

* Cleanup

* Use doArtilleryFire which was fixed in 1.68

* hitreactions - Close map on hit (#5099)

* Wiki AtragMx minor changes (#5111)

* fix markdown syntax

* Update atragmx.md

* AB: Lythium latitude (#5109)

* Update fnc_getMapData.sqf

* bump1

* bump2

* AB: update compat R3F v3.5 (#5097)

* Update CfgWeapons.hpp

* Update CfgAmmo.hpp

* Update CfgAmmo.hpp

* Update CfgWeapons.hpp

* Update CfgWeapons.hpp

* Update CfgWeapons.hpp

* Update CfgWeapons.hpp

* Update CfgWeapons.hpp

* fix missing `{`

* fix `dispersion`

* fix space

* AtragMx: BC G1 .338LM API526 (#5069)

* Update fnc_initGunList.sqf

* Update fnc_initGunList.sqf

* C1 coefficient 0.58

* Jerry Can - only set global var on server (#5131)

* Add Arma 3 Issues breaking ace_rearm (#5150)

* MapGesutres - Fix dependency on maptools (#5154)

* MapGesutres - Use getVariable to remove dependency

* Use isModLoaded

* Add missing base class to r3f compat (#5156)

* Disable g-forces for UAV AI (#5094)

* Increase max weapon index for large modsets (#5054)

* Disable rearm,refuel on dead vehicles (#5158)

* SQF Lint Cleanup Pass (#5157)

* SQF Lint Cleanup Pass

* Fix var in TRACE

* Add basic python tool

* Simplify

* Hit space key 6 times

* Fix error in dropNozzle

* handle error message exceptions

* Fix py

* 1.70 CMs - Remove countermeasure mode switching (#5163)

* 1.70 FCS - Remove ACE_FCS from most vics (#5152)

* Handle 1.70 FCS

* Cleanup CfgWeapons

* Add warning for discreteDistance

* 1.70 CMs - Remove countermeasure mode switching (#5163)

* 1.70 FCS - Remove ACE_FCS from most vics (#5152)

* Handle 1.70 FCS

* Cleanup CfgWeapons

* Add warning for discreteDistance

* Realistic names for 1.70 pylon magazines (#5165)

* Realistic names for 1.70 pylon magazines (#5165)

* Fix gatling_30mm UBC for 1.70 (#5125)

* Fix gatling_30mm UBC for 1.70

* Fix verEsion

* Fix gatling_30mm UBC for 1.70 (#5125)

* Fix gatling_30mm UBC for 1.70

* Fix verEsion

* Prep 3.9.2 (version numbers)

* Update required Arma 3 version to 1.70

* Prepare release 3.9.2

* Update helmet hearing protection for RHS-US (#5146)

* Update helmet hearing protection for RHS-US

* Hearing - Add standardized helmet protection macros

* Tweak peltor vol

* Disable aircraft flightmodel changes (#5167)

* Disable aircraft flightmodel changes

* Remove files

* Fix merge, cbaEvents, macros, cleanup

* Use correct TransportX type for ace items (#5168)

* Use correct TransportX type for ace items

* Fix messing with flare colors

* Just warn for old compat pbos (#5177)

* Just warn for old compat pbos

* Use CBA_fnc_error big warning box

* fix caps

* Italian translation (Add & Fix) (#5193)

Added Italian translation for:
-advanced fatigue
-advanced throwing
-cook off
-dogtags
-gunbag
-ui-scopes and much more...

Fixed a lot of grammatical errors

* Zeus updated (#5198)

Fixes and adds in the zeus section

* Add AtixNeon to AUTHORS.txt

* Add realistic names to dynamic loadout classes (#5185)

* RHS CH-47 - use animationSourcePhase (#5201)

* Fix Zeus teleport players module (#5175)

* Add dummy function to zeus UI modules

This removes the message about the module having no function when in SP.

* Fix zeus teleport module for non-local units

BI made a change to the function this module was using. So now it has to
be ran local to the player in question.

* Adv Throw - Handle getShotParents being [] (#5210)

* Correct compat classnames (#5204)

* Linking belt refactoring (#5206)

* Linking belt refatoring

startLinking belt now uses canLinkBelt so if condition needs to be changed then you can do it on one position.

* Fixed requested change

Fixed requested change

* AdvThrow - Handle bugged currentThrowable (#5216)

* Fix AB water vapor pressure calc (#4956)

* Fix AB water vapor pressure calc

* Fix calc in weather

* Cleanup usage of constant

* Add RVExtensionVersion, Rebuild AB DLLs

* CH53 Fastroping and other fixes (#4713)

* CH53 Fastroping and Fixes

Added fastrope points for CH53.

The getOut EH is required to keep the doors open when a unit exits the
vehicle and it uses the vehicle animation "doorHandler_R" or
"doorHandler_L" as a placeholder to determine if the doors are
open(1)/close(0).
eg. In UH1Y (Unarmed), when the right door is opened using the
useraction, "doorRB" and "doorHandler_R" will be set to 1.

* Changed TAB to 4 spaces

* Moved bracket to follow guidelines

* Refuel for non-AllVehicle objects (#5151)

* Enabled refuel for non-AllVehicles objects. Un-magic'd refuel progress timer.

* Changed helper attach position to centre of object. Offset applied when rope attached.

* Made fully compatible with non-thingX simulation types. Removed destruction effects of helper object.

* Using arma model. Removed ACE_Actions from helper object.

* Correct use of hideObjectGlobal

* Remove actions, cargo, repair on helper

* Switched helper to be setVar'd on the nozzle object

* Linkingbelt refactoring (#5213)

* Linking belt refatoring

startLinking belt now uses canLinkBelt so if condition needs to be changed then you can do it on one position.

* Fixed requested change

Fixed requested change

* simplified it even more

canLinkBelt now returns a value over 0 if success and -1 if something is not right.

* Fixed bug where if error we would not exit

Fixed bug where if error we would not exit

* changed name on canLinkBelt

Changed name to better reflect the function of the function.

* Author hype

* fixed return value info

fixed return value info

* fix header

* Fix ammo count hidden in UAVs (#5222)

* Fix ammo count hidden in UAVs

* Switch to ace_reload condition

* Add arsenal zeus modules (#4576)

* Add zeus arsenal modules

* Add french strings to arsenal modules

* Fix typo in french string

* Make changes to fit review

* locality check, use showMessage, set categroy

* Cargo load menu overhaul (#4871)

* Add submenu with vehicles to cargo load menu

* replace private ARRAY with keyword

* fix ace function macro using

* filter vehicles without cargo

* add Load condition, clean params, fix param reusing

* replace nearEntities with nearestObjects, add macro

* optimize, del magic, replace count with forEach

* del unused functions

* del useless _this parameter

* Cleanup aircraft (#5197)

* Refuel - Add setting for hose length (#5224)

* Remove unit from laser pointer list if laser removed (#5190)

* Handle escape menu with toggle iteractMenu (#5144)

* Handle escape menu with toggle iteractMenu

* Work on non-cursor menu

* Close cursor menu if open when switching

* Update fnc_handleEscapeMenu.sqf

* French translation missing (#5217)

* French translation for the two last entries

* French translation for the last entrie

* French Translation for the first entrie

* correct the spelling error

* Lower minimum speed limiter speed to 5 km/h (#5065)

* Update fnc_speedLimiter.sqf

* No minimum speed needed anymore

Minimum speed limit of 10 km/h was needed in the past due to engine limitations. Multiple user tests have shown that the minimum speed is not needed anymore. The new minimum of 0 km/h allows for example setting walking speed for vehicles (<10 km/h).

* Change minimum required speed for speed limiter to 3 km/h

To avoid problems with negative speeds (driving backwards) and zero speed, the current change switches from 10 km/h minimum speed to 3 km/h minimum speed. This seems to be the optimal solution to allow all relevant speeds including walking speed.

* Changed minimum required speed to 5 km/h

Officially the minimum required speed is 10 km/h in the master. Lower minimum needed to set car speed to walking speed of accompanying soldiers. Problems have been reported with 3 kmh/ using cars like ATVs.  Thus the new commit is set to 5 km/h minimum speed. Not tested with ATVs yet.

* Minor fixes, cleanup, add lint ignore directives (#5176)

- Fix bug in getNumberMagazinesIn (func not used)
- Fix bug in seekerFindLaserSpot (func not used yet)
- Everything else is just cleanup

* Fix zeus interaction locality (#5214)

- The `setUnitPos`, `setFormation`, `setSpeedMode` and `setBehaviour` commands require local arguments
- The waypoint equivalents require server execution
- Add conditions to only show interactions when the appropriate things are selected

* Add cargo eden attributes (#4780)

- Add a ace_cargo_space attribute to vehicles to alter how much cargo they can carry.
- Add an ace_cargo_size attribute to objects to alter how much cargo space they consume.
- Add two public functions `fnc_setSize.sqf` and `fnc_setSpace.sqf` to update the cargo size/space respectively of any given object.
- Deprecate cargo makeLoadable module and public function.
- Added some macros to get the space/size of a config, making code more readable in places.

* Fix IVs not showing in patient display (#5230)

Fix #5192 for 3.10

* Remove submuntion ammo changes to A-10 (#5231)

* Remove changes to Aircraft cannons

* keep ammo changes, i guess

* Fix zeus RC units using player damage treshold (#5219)

* Fix zeus RC units using player damage treshold

Closes #5218

* add pabstmirrors suggestion

use gvar

* Implement new cook off SFX (#5179)

* Implement new cook off SFX with variants
* Add hybrid V to authors

* Open glass and CUP doors (#5226)

* Fixed glassdoor

Fixed so glassdoor now works with ace slow open.

* Made it more pretty with new file

Made it more pretty with new file

* Tidy up a bit

* Removed white space

* Replace tabs with spaces

Replace tabs with spaces

* Simplified and added comments

* Changes + was stupid was commit

Changes to go with code guidlines and extra check if door is empty

* Tabs to spaces

* Small fixes + Fixed so CUP houses now works

Fixed so CUP houses now works

* Remove todo

* Fixed requested changes

* Removed whitespaces

* Vehicle quick mount (#4931)

* Add quickmount files from ACEX

* Rename to ACE3, Streamline, Use clientInit

* Rename functions to ACE3, Fix crew getting

* Fix enabled text

* Fix texts

* Remove spaces

* Rework seat search

* NLAW - Predicted Line Of Sight Guidance and Overfly Attack Mode (#4791)

* NLAW Prototype

* Make AI Compatible

* Add Overfly Top Attack Mode

* Limit Max Deflection

* Base prediction on AI skill

* Generic cycle attack profile key for missile guidance

* Add hint for weapons without huds

* Configure for attack cycle key

* Finish OTA ammo effects

* Cleanup

* Arm at 20m

* Disable Debug

* No models for short lived sub-ammos

* Fix Korean strings

* Change AI randomization to use skillFinal

* Add wiki doc for nlaw

* Cleanup

* Cleanup

* Cleanup

* Hellfire missiles (#4679)

* Hellfire

* Cleanup dev macros

* Cleanup some debug

* Add base interaction node for firemode actions

* Handle bad data in attack profile variable

* Skip ammo checks (returns bad data on added weaps)

* Add mags sizes for apache

* Add Hellfire Wiki Doc

* Cleanup doc

* Add pylon support

* Add support for pilot controlled weapons

* Add label to pylon mags

* Cleanup vehicle configs, autoAdd laser des, fix ineractions for driver

* [Docs] Update component dependencies
Automatically committed through Travis CI.

[ci skip]

* [Docs] Update component dependencies
Automatically committed through Travis CI.

[ci skip]

* [Docs] Update component dependencies
Automatically committed through Travis CI.

[ci skip]

* Fix spectator icons and camera jitter (#5067)

* Use `Visual` commands for icons and external camera position

* Fix accidental numerical change (#5235)

* Fix being unable to throw last mag (#5242)

* Italian Translation Update (#5243)

Added Italian translation for:
-cargo
-hellfire
-nlaw
-quickmount
-refuel
-zeus

* Cleanup unused hellfire vic stringtable (#5245)

* Fix weapon select grenade count wehn using advThrow (#5244)

* Raise lower carry object (#5234)

* Fix to move items up and down

* Fixed private

* Fixed public bug

* Inserted Pabst solution

* Remove extra space

* Fixed request + added author

* Fixed to correct comment

* Tweeked comment again

* Added more info about indentations, tabs and EditorConfig (#5249)

* Added more info about indentations, tabs and EditorConfig

* Fixed original misspelling "Every"

* Added Pylon weapon as a valid weapon (#5250)

* Disable dragging on big AA Turrets (#5251)

* note required PboProject version (#5256)

* note required PboProject version

* Update setting-up-the-development-environment.md

* Conform function headers to coding guidelines (#5255)

* Fixed headers to work with silentspike python script

* Fixed rest of the files

* Fixed ace-team

* Rearm - Pylon Support and Dynamically Add Supply Actions (#5183)

* Dynamic Add

* Support 1.70 Pylon Loadouts

* Properly handle old compat pbos - Update RHS Compat

* Re-add documentation

* cleanup headers (note from other pr)

* Cleanup

* Fix var spelling

* Fix minor debug that got left in (#5260)

* Repair specialist rename, fixes #3186 (#5248)

* Changed name

* Fixed Enginner only german and removed big letters

* Added docs + PR fixes #3186

* Removed other languages

* Fixed request and added comment

* Replace all usage of term "Specialist"

* Add Ability to Change Repair Times of Vehicles (#5205)

* Add ability to change repair time based on vehicle

* Much better

* Unary command not function

* Allow changing of track or wheel object per vehicle

* Lot more complicated than I thought

* Fix some event names on wiki (#5254)

* Fix some event names on wiki

* Switch callable events to functions

* Remove maximum distance of attach to vehicle action (#5262)

* Remove maximum distance of attachi to vehicle action

* Readd max distance, 10 meters to allow usage with big vehicles but also end placement system if too far

* Update kestrel4500.md (#5228)

* Add repair feature to wiki (#5247)

* Add repair feature to wiki

* Add blank lines, use adv engineer

* Add repair framework page

* Fix Large IED defuse distance (#5261)

* Fix for large-ied bug because underwater thing have changed

* Added size of IED in config and determine defuse range

* Added typo

* Added help text to wiki about defusing

* Better English

* Increased the defusal range on large bombs make it similar to small ieds

* Add wiki doc

* Use getUnitTrait for medics, engineers and EOD (#5246)

* Increase required CBA version to 3.3.1

* Prepare version 3.10.0

* Update cargo docs with new public functions (#5266)

* Update cargo docs with new public functions
* Add myself to squad.xml

* Italian translation updated and fixed (#5268)

Added newest lines for:
-Rearm
-Repair

* Update homepage version numbers #4692
[ci skip]

* Fix isFeatureCameraActive for Spectator enhacements (#5269)

* fix isFeatureCameraActive for spec 

ace_spectator_camera was removed in 77c2b99ee5 .
spectator module initializes three separate cameras instead, of which for example ace_spectator_freeCamera can be used to check if spec mode is active

* use ace_spectator_isSet to check for spec mode

* Prepare release 3.10.0 (build 2)

* fix video link (#5276)

* Jpn translate 73bd37 (#5277)

* Update the Jpn translation for cargo

Update the Japanese translation for cargo

* Add the Jpn translation for hellfire

Add the Japanese translation for hellfire

* Add the Jpn translation for nlaw

Add the Japanese translation for nlaw

* Add the Jpn translation for rearm

Add the Japanese translation for rearm

* Update the Jpn translation for refuel

Update the Japanese translation for refuel

* Update the Jpn translation for repair

Update the Japanese translation for repair

* Update the Jpn translation for zeus

Update the Japanese translation for zeus

* gos ndjenahoud latitude (#5275)

* Count untraced errors in make.py (#5265)

* Count untraced errors (any errors besides failed PBO builds and missinf files) in make.py

* Fix error count and prettify printing

* Cleanup heli pod actions (#5271)

Close #4783
- Make taru pods non loadable
- Make taru pods non draggable
- Increase interaction range for huron pods

* Make array of cargo holders dynamic (#5274)

* Jpn translate feee7f5 (#5280)

* Add the Jpn translation for quickmount

Add the Japanese translation for quickmount

* Update the Jpn translation for repair

Update the Japanese translation for repair

* Fix isFeatureCameraActive (#5291)

* Fix isFeatureCameraActive (#5291)

* Prepare release 3.10.1

* Add malden to getMapData (#5297)

* Add malden to getMapData

* Merge with CWR2 malden

* Add malden to getMapData (#5297)

* Add malden to getMapData

* Merge with CWR2 malden

* Add Chinese Traditional & Simplified Localization (#5295)

Add Chinese Traditional & Simplified Localization

* compats - Fix config name for resupply vics (#5300)

* Modify JoinGroup action to show group name (#5287)

* Modify JoinGroup action to show group name

* header

* Header

* Repair - Handle depends = "0" (#5283)

* Repair - Handle depends = "0"

* quote

* Refuel - change nozzle position calc (#5279)

* Handle ace_cargo_canLoad set to scalar (#5306)

* Add XEH to ace_module (#5304)

* Changed hellfire interaction check & added N variant (#5294)

* Changed weapon check for interaction to generic ace_hellfire_enabled value

* Added November hellfire variant

* Cleanup

* +1

* Japanese translation d02dacf (#5309)

* add the Jpn translation for repair

add the Japanese translation for repair

* minor fix to Jpn translation for medical

minor fix to Jpn translation for medical

* Correct new Chinese stringtable identifiers (#5334)

* Fix the issue that SFX variants are not global  (#5335)

* Fix the issue that SFX variants are not global what means that different players can get different pressure Cookoff sounds

to prevent that i split up the sounds in 3 types and used a Weighted select to have the befor used values back

* add todo for 1.74

* inherit in CfgSFX from 1 class

* fix #5324 (#5325)

* Minor update Wiki AtragMx (#5322)

* update wiki atragmx

* Add files via upload

* Fix broken fonts for map gps display (#5337)

EtelkaNarrowMediumPro broke with 1.72 hotfix

* Handle locked doors (#5345)

* Allow repairing from slingload pods (#5316)

* Add cargo space to huron containers (#5315)

* Rallypoints- Don't modify setVar if already set (#5347)
2017-07-10 01:27:29 -07:00

1520 lines
58 KiB
Python

#!/usr/bin/env python3
# vim: set fileencoding=utf-8 :
# make.py
# An Arma 3 addon build system
###############################################################################
# The MIT License (MIT)
# Copyright (c) 2013-2014 Ryan Schultz
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
###############################################################################
__version__ = "0.9"
import sys
if sys.version_info[0] == 2:
print("Python 3 is required.")
sys.exit(1)
import os
import os.path
import shutil
import platform
import glob
import subprocess
import hashlib
import configparser
import json
import traceback
import time
import timeit
import re
import fileinput
if sys.platform == "win32":
import winreg
######## GLOBALS #########
project = "@ace"
project_version = "3.0.0"
arma3tools_path = ""
work_drive = ""
module_root = ""
make_root = ""
release_dir = ""
module_root_parent = ""
optionals_root = ""
key_name = "ace"
key = ""
dssignfile = ""
prefix = "ace"
pbo_name_prefix = "ace_"
signature_blacklist = ["ace_server.pbo"]
importantFiles = ["mod.cpp", "README.md", "docs\\README_DE.md", "docs\\README_PL.md", "AUTHORS.txt", "LICENSE", "logo_ace3_ca.paa", "meta.cpp"]
versionFiles = ["README.md", "docs\\README_DE.md", "docs\\README_PL.md", "mod.cpp"]
ciBuild = False # Used for CI builds
###############################################################################
# http://akiscode.com/articles/sha-1directoryhash.shtml
# Copyright (c) 2009 Stephen Akiki
# MIT License (Means you can do whatever you want with this)
# See http://www.opensource.org/licenses/mit-license.php
# Error Codes:
# -1 -> Directory does not exist
# -2 -> General error (see stack traceback)
def get_directory_hash(directory):
directory_hash = hashlib.sha1()
if not os.path.exists (directory):
return -1
try:
for root, dirs, files in os.walk(directory):
for names in files:
path = os.path.join(root, names)
try:
f = open(path, 'rb')
except:
# You can't open the file for some reason
f.close()
continue
while 1:
# Read file in as little chunks
buf = f.read(4096)
if not buf: break
new = hashlib.sha1(buf)
directory_hash.update(new.digest())
f.close()
except:
# Print the stack traceback
traceback.print_exc()
return -2
retVal = directory_hash.hexdigest()
#print_yellow("Hash Value for {} is {}".format(directory,retVal))
return directory_hash.hexdigest()
def Fract_Sec(s):
temp = float()
temp = float(s) / (60*60*24)
d = int(temp)
temp = (temp - d) * 24
h = int(temp)
temp = (temp - h) * 60
m = int(temp)
temp = (temp - m) * 60
sec = temp
return d,h,m,sec
#endef Fract_Sec
# Copyright (c) André Burgaud
# http://www.burgaud.com/bring-colors-to-the-windows-console-with-python/
if sys.platform == "win32":
from ctypes import windll, Structure, c_short, c_ushort, byref
SHORT = c_short
WORD = c_ushort
class COORD(Structure):
"""struct in wincon.h."""
_fields_ = [
("X", SHORT),
("Y", SHORT)]
class SMALL_RECT(Structure):
"""struct in wincon.h."""
_fields_ = [
("Left", SHORT),
("Top", SHORT),
("Right", SHORT),
("Bottom", SHORT)]
class CONSOLE_SCREEN_BUFFER_INFO(Structure):
"""struct in wincon.h."""
_fields_ = [
("dwSize", COORD),
("dwCursorPosition", COORD),
("wAttributes", WORD),
("srWindow", SMALL_RECT),
("dwMaximumWindowSize", COORD)]
# winbase.h
STD_INPUT_HANDLE = -10
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12
# wincon.h
FOREGROUND_BLACK = 0x0000
FOREGROUND_BLUE = 0x0001
FOREGROUND_GREEN = 0x0002
FOREGROUND_CYAN = 0x0003
FOREGROUND_RED = 0x0004
FOREGROUND_MAGENTA = 0x0005
FOREGROUND_YELLOW = 0x0006
FOREGROUND_GREY = 0x0007
FOREGROUND_INTENSITY = 0x0008 # foreground color is intensified.
BACKGROUND_BLACK = 0x0000
BACKGROUND_BLUE = 0x0010
BACKGROUND_GREEN = 0x0020
BACKGROUND_CYAN = 0x0030
BACKGROUND_RED = 0x0040
BACKGROUND_MAGENTA = 0x0050
BACKGROUND_YELLOW = 0x0060
BACKGROUND_GREY = 0x0070
BACKGROUND_INTENSITY = 0x0080 # background color is intensified.
stdout_handle = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute
GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo
def get_text_attr():
"""Returns the character attributes (colors) of the console screen
buffer."""
csbi = CONSOLE_SCREEN_BUFFER_INFO()
GetConsoleScreenBufferInfo(stdout_handle, byref(csbi))
return csbi.wAttributes
def set_text_attr(color):
"""Sets the character attributes (colors) of the console screen
buffer. Color is a combination of foreground and background color,
foreground and background intensity."""
SetConsoleTextAttribute(stdout_handle, color)
###############################################################################
def find_bi_tools(work_drive):
"""Find BI tools."""
reg = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER)
try:
k = winreg.OpenKey(reg, r"Software\bohemia interactive\arma 3 tools")
arma3tools_path = winreg.QueryValueEx(k, "path")[0]
winreg.CloseKey(k)
except:
raise Exception("BadTools","Arma 3 Tools are not installed correctly or the P: drive needs to be created.")
addonbuilder_path = os.path.join(arma3tools_path, "AddonBuilder", "AddonBuilder.exe")
dssignfile_path = os.path.join(arma3tools_path, "DSSignFile", "DSSignFile.exe")
dscreatekey_path = os.path.join(arma3tools_path, "DSSignFile", "DSCreateKey.exe")
cfgconvert_path = os.path.join(arma3tools_path, "CfgConvert", "CfgConvert.exe")
if os.path.isfile(addonbuilder_path) and os.path.isfile(dssignfile_path) and os.path.isfile(dscreatekey_path) and os.path.isfile(cfgconvert_path):
return [addonbuilder_path, dssignfile_path, dscreatekey_path, cfgconvert_path]
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
if regKey == "HKCU":
reg = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER)
stop = True
else:
reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
try:
try:
k = winreg.OpenKey(reg, r"Software\Wow6432Node\Mikero\pboProject")
except FileNotFoundError:
k = winreg.OpenKey(reg, r"Software\Mikero\pboProject")
try:
pboproject_path = winreg.QueryValueEx(k, "exe")[0]
winreg.CloseKey(k)
print("Found pboproject.")
except:
print_error("Could not find pboProject.")
try:
k = winreg.OpenKey(reg, r"Software\Wow6432Node\Mikero\rapify")
except FileNotFoundError:
k = winreg.OpenKey(reg, r"Software\Mikero\rapify")
try:
rapify_path = winreg.QueryValueEx(k, "exe")[0]
winreg.CloseKey(k)
print("Found rapify.")
except:
print_error("Could not find rapify.")
try:
k = winreg.OpenKey(reg, r"Software\Wow6432Node\Mikero\MakePbo")
except FileNotFoundError:
k = winreg.OpenKey(reg, r"Software\Mikero\MakePbo")
try:
makepbo_path = winreg.QueryValueEx(k, "exe")[0]
winreg.CloseKey(k)
print("Found makepbo.")
except:
print_error("Could not find makepbo.")
except:
if stop == True:
raise Exception("BadDePBO", "DePBO tools not installed correctly")
return -1
#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":
if color == "green":
set_text_attr(FOREGROUND_GREEN | get_text_attr() & 0x0070 | FOREGROUND_INTENSITY)
elif color == "yellow":
set_text_attr(FOREGROUND_YELLOW | get_text_attr() & 0x0070 | FOREGROUND_INTENSITY)
elif color == "red":
set_text_attr(FOREGROUND_RED | get_text_attr() & 0x0070 | FOREGROUND_INTENSITY)
elif color == "blue":
set_text_attr(FOREGROUND_BLUE | get_text_attr() & 0x0070 | FOREGROUND_INTENSITY)
elif color == "reset":
set_text_attr(FOREGROUND_GREY | get_text_attr() & 0x0070)
elif color == "grey":
set_text_attr(FOREGROUND_GREY | get_text_attr() & 0x0070)
else :
if color == "green":
sys.stdout.write('\033[92m')
elif color == "red":
sys.stdout.write('\033[91m')
elif color == "blue":
sys.stdout.write('\033[94m')
elif color == "reset":
sys.stdout.write('\033[0m')
def print_error(msg):
color("red")
print("ERROR: {}".format(msg))
color("reset")
global printedErrors
printedErrors += 1
def print_green(msg):
color("green")
print(msg)
color("reset")
def print_blue(msg):
color("blue")
print(msg)
color("reset")
def print_yellow(msg):
color("yellow")
print(msg)
color("reset")
def copy_important_files(source_dir,destination_dir):
originalDir = os.getcwd()
# Copy importantFiles
try:
print_blue("\nSearching for important files in {}".format(source_dir))
print("Source_dir: {}".format(source_dir))
print("Destination_dir: {}".format(destination_dir))
for file in importantFiles:
filePath = os.path.join(module_root_parent, file)
if os.path.exists(filePath):
print_green("Copying file => {}".format(filePath))
shutil.copy(os.path.join(source_dir,filePath), destination_dir)
else:
missingFiles.append("{}".format(filePath))
print_error("Failed copying file => {}".format(filePath))
except:
print_error("COPYING IMPORTANT FILES.")
raise
# Copy all extension DLL's
try:
os.chdir(os.path.join(source_dir))
print_blue("\nSearching for DLLs in {}".format(os.getcwd()))
filenames = glob.glob("*.dll")
if not filenames:
print ("Empty SET")
for dll in filenames:
print_green("Copying dll => {}".format(os.path.join(source_dir,dll)))
if os.path.isfile(dll):
shutil.copyfile(os.path.join(source_dir,dll),os.path.join(destination_dir,dll))
except:
print_error("COPYING DLL FILES.")
raise
finally:
os.chdir(originalDir)
def copy_optionals_for_building(mod,pbos):
src_directories = os.listdir(optionals_root)
current_dir = os.getcwd()
print_blue("\nChecking optionals folder...")
try:
#special server.pbo processing
files = glob.glob(os.path.join(release_dir, project, "optionals", "*.pbo"))
for file in files:
file_name = os.path.basename(file)
#print ("Adding the following file: {}".format(file_name))
pbos.append(file_name)
pbo_path = os.path.join(release_dir, project, "optionals", file_name)
sigFile_name = file_name +"."+ key_name + ".bisign"
sig_path = os.path.join(release_dir, project, "optionals", sigFile_name)
if (os.path.isfile(pbo_path)):
print("Moving {} for processing.".format(pbo_path))
shutil.move(pbo_path, os.path.join(release_dir, project, "addons", file_name))
if (os.path.isfile(sig_path)):
#print("Moving {} for processing.".format(sig_path))
shutil.move(sig_path, os.path.join(release_dir, project, "addons", sigFile_name))
except:
print_error("Error in moving")
raise
finally:
os.chdir(current_dir)
try:
for dir_name in src_directories:
mod.append(dir_name)
#userconfig requires special handling since it is not a PBO source folder.
#CfgConvert fails to build server.pbo if userconfig is not found in P:\
if (dir_name == "userconfig"):
if (os.path.exists(os.path.join(release_dir, project, "optionals", dir_name))):
shutil.rmtree(os.path.join(release_dir, project, "optionals", dir_name), True)
shutil.copytree(os.path.join(optionals_root,dir_name), os.path.join(release_dir, project, "optionals", dir_name))
destination = os.path.join(work_drive,dir_name)
else:
destination = os.path.join(module_root,dir_name)
print("Temporarily copying {} => {} for building.".format(os.path.join(optionals_root,dir_name),destination))
if (os.path.exists(destination)):
shutil.rmtree(destination, True)
shutil.copytree(os.path.join(optionals_root,dir_name), destination)
except:
print_error("Copy Optionals Failed")
raise
finally:
os.chdir(current_dir)
def cleanup_optionals(mod):
print("")
try:
for dir_name in mod:
#userconfig requires special handling since it is not a PBO source folder.
if (dir_name == "userconfig"):
destination = os.path.join(work_drive,dir_name)
else:
destination = os.path.join(module_root,dir_name)
print("Cleaning {}".format(destination))
try:
file_name = "{}{}.pbo".format(pbo_name_prefix,dir_name)
src_file_path = os.path.join(release_dir, project, "addons", file_name)
dst_file_path = os.path.join(release_dir, project, "optionals", file_name)
sigFile_name = "{}.{}.bisign".format(file_name,key_name)
src_sig_path = os.path.join(release_dir, project, "addons", sigFile_name)
dst_sig_path = os.path.join(release_dir, project, "optionals", sigFile_name)
if (os.path.isfile(src_file_path)):
#print("Preserving {}".format(file_name))
os.renames(src_file_path,dst_file_path)
if (os.path.isfile(src_sig_path)):
#print("Preserving {}".format(sigFile_name))
os.renames(src_sig_path,dst_sig_path)
except FileExistsError:
print_error("{} already exists".format(file_name))
continue
shutil.rmtree(destination)
except FileNotFoundError:
print_yellow("{} file not found".format(file_name))
except:
print_error("Cleaning Optionals Failed")
raise
def purge(dir, pattern, friendlyPattern="files"):
print_green("Deleting {} files from directory: {}".format(friendlyPattern,dir))
if os.path.exists(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
global signature_blacklist
ret = 0
baseFile = os.path.basename(file_name)
#print_yellow("Sig_fileName: {}".format(baseFile))
if not (baseFile in signature_blacklist):
print("Signing with {}.".format(key))
ret = subprocess.call([dssignfile, key, file_name])
if ret == 0:
return True
else:
return False
def check_for_obsolete_pbos(addonspath, file):
module = file[len(pbo_name_prefix):-4]
if not os.path.exists(os.path.join(addonspath, module)):
return True
return False
def backup_config(module):
#backup original $PBOPREFIX$
global work_drive
global prefix
try:
configpath = os.path.join(work_drive, prefix, module, "$PBOPREFIX$")
if os.path.isfile(configpath):
shutil.copyfile(configpath, os.path.join(work_drive, prefix, module, "$PBOPREFIX$.backup"))
else:
print_error("$PBOPREFIX$ Does not exist for module: {}.".format(module))
except:
print_error("Error creating backup of $PBOPREFIX$ for module {}.".format(module))
return True
def addon_restore(modulePath):
#restore original $PBOPREFIX$
try:
if os.path.isfile(os.path.join(modulePath, "$PBOPREFIX$.backup")):
if os.path.isfile(os.path.join(modulePath, "$PBOPREFIX$")):
os.remove(os.path.join(modulePath, "$PBOPREFIX$"))
os.rename(os.path.join(modulePath, "$PBOPREFIX$.backup"), os.path.join(modulePath, "$PBOPREFIX$"))
except:
print_yellow("Some error occurred. Check your addon folder {} for integrity".format(modulePath))
return True
def get_project_version(version_increments=[]):
global project_version
versionStamp = project_version
#do the magic based on https://github.com/acemod/ACE3/issues/806#issuecomment-95639048
try:
scriptModPath = os.path.join(module_root, "main\script_version.hpp")
if os.path.isfile(scriptModPath):
f = open(scriptModPath, "r")
hpptext = f.read()
f.close()
if hpptext:
majorText = re.search(r"#define MAJOR (.*\b)", hpptext).group(1)
minorText = re.search(r"#define MINOR (.*\b)", hpptext).group(1)
patchText = re.search(r"#define PATCHLVL (.*\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:
versionStamp = "{}.{}.{}.{}".format(majorText,minorText,patchText,buildText)
else:
print_error("A Critical file seems to be missing or inaccessible: {}".format(scriptModPath))
raise FileNotFoundError("File Not Found: {}".format(scriptModPath))
except Exception as e:
print_error("Get_project_version error: {}".format(e))
print_error("Check the integrity of the file: {}".format(scriptModPath))
versionStamp = project_version
print_error("Resetting to the default version stamp: {}".format(versionStamp))
input("Press Enter to continue...")
print("Resuming build...")
print_yellow("{} VERSION set to {}".format(project.lstrip("@").upper(),versionStamp))
project_version = versionStamp
return project_version
def replace_file(filePath, oldSubstring, newSubstring):
for line in fileinput.input(filePath, inplace=True):
# Use stdout directly, print() adds newlines automatically
sys.stdout.write(line.replace(oldSubstring,newSubstring))
def set_version_in_files():
newVersion = project_version # MAJOR.MINOR.PATCH.BUILD
newVersionArr = newVersion.split(".")
newVersionShort = ".".join((newVersionArr[0],newVersionArr[1],newVersionArr[2])) # MAJOR.MINOR.PATCH
# Regex patterns
pattern = re.compile(r"([\d]+\.[\d]+\.[\d]+\.[\d]+)") # MAJOR.MINOR.PATCH.BUILD
patternShort = re.compile(r"([\d]+\.[\d]+\.[\d]+)") # MAJOR.MINOR.PATCH
# Change versions in files containing version
for i in versionFiles:
filePath = os.path.join(module_root_parent, i)
try:
# Save the file contents to a variable if the file exists
if os.path.isfile(filePath):
f = open(filePath, "r+")
fileText = f.read()
f.close()
if fileText:
# Version string files
# Search and save version stamp
versionsFound = re.findall(pattern, fileText) + 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
for versionFound in versionsFound:
if versionFound:
# Use the same version length as the one found
newVersionUsed = "" # In case undefined
if versionFound.count(".") == newVersion.count("."):
newVersionUsed = newVersion
if versionFound.count(".") == newVersionShort.count("."):
newVersionUsed = newVersionShort
# 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:
# Temporary file is still "in use" by Python, pass this exception
pass
except Exception as e:
print_error("set_version_in_files error: {}".format(e))
raise
return True
def stash_version_files_for_building():
try:
for file in versionFiles:
filePath = os.path.join(module_root_parent, file)
if os.path.exists(filePath):
# Take only file name for stash location if in subfolder (otherwise it gets removed when removing folders from release dir)
if "\\" in file:
count = file.count("\\")
file = file.split("\\", count)[-1]
stashPath = os.path.join(release_dir, file)
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:
print_error("Stashing version files failed")
raise
# Set version
set_version_in_files()
return True
def restore_version_files():
try:
print_blue("\nRestoring version files...")
for file in versionFiles:
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)
if "\\" in file:
count = file.count("\\")
file = file.split("\\", count)[-1]
stashPath = os.path.join(release_dir, file)
if os.path.exists(filePath):
print("Restoring {}".format(filePath))
shutil.move("{}.bak".format(stashPath), filePath)
except:
print_error("Restoring version files failed")
raise
return True
def get_private_keyname(commitID,module="main"):
global pbo_name_prefix
global project_version
keyName = str("{prefix}{version}-{commit_id}".format(prefix=pbo_name_prefix,version=project_version,commit_id=commitID))
return keyName
def get_commit_ID():
# Get latest commit ID
global make_root
curDir = os.getcwd()
commit_id = ""
try:
# Verify if Git repository
gitpath = os.path.join(os.path.dirname(make_root), ".git")
assert os.path.exists(gitpath)
# Try to get commit ID through Git client
os.chdir(make_root)
commit_id = subprocess.check_output(["git", "rev-parse", "HEAD"])
commit_id = str(commit_id, "utf-8")[:8]
except FileNotFoundError:
# Try to get commit ID from git files (subprocess failed - eg. no Git client)
head_path = os.path.join(gitpath, "HEAD")
if os.path.exists(head_path):
with open(head_path, "r") as head_file:
branch_path = head_file.readline().split(": ")
# Commit ID is written in HEAD file directly when in detached state
if len(branch_path) == 1:
commit_id = branch_path[0]
else:
branch_path = branch_path[-1].strip()
ref_path = os.path.join(gitpath, branch_path)
if os.path.exists(ref_path):
with open(ref_path, "r") as ref_file:
commit_id = ref_file.readline()
if commit_id != "":
commit_id = commit_id.strip()[:8]
else:
raise
except:
# All other exceptions (eg. AssertionException)
if commit_id == "":
raise
finally:
pass
if commit_id == "":
print_error("Failed to determine commit ID - folder is not a Git repository.")
commit_id = "NOGIT"
os.chdir(curDir)
print_yellow("COMMIT ID set to {}".format(commit_id))
return commit_id
def version_stamp_pboprefix(module,commitID):
### Update pboPrefix with the correct version stamp. Use commit_id as the build number.
#This function will not handle any $PBOPREFIX$ backup or cleanup.
global work_drive
global prefix
configpath = os.path.join(work_drive, prefix, module, "$PBOPREFIX$")
try:
f = open(configpath, "r")
configtext = f.read()
f.close()
if configtext:
if re.search(r"version=(.*?)$", configtext, re.DOTALL):
if configtext:
configtext = re.sub(r"version=(.*?)$", "version={}\n".format(commitID), configtext, flags=re.DOTALL)
f = open(configpath, "w")
f.write(configtext)
f.close()
else:
os.remove(os.path.join(work_drive, prefix, module, "$PBOPREFIX$"))
os.rename(os.path.join(work_drive, prefix, module, "$PBOPREFIX$.backup"), os.path.join(work_drive, prefix, module, "$PBOPREFIX$"))
else:
if configtext:
#append version info
f = open(configpath, "a")
f.write("\nversion = {}".format(commitID))
f.close()
else:
os.remove(os.path.join(work_drive, prefix, module, "$PBOPREFIX$"))
os.rename(os.path.join(work_drive, prefix, module, "$PBOPREFIX$.backup"), os.path.join(work_drive, prefix, module, "$PBOPREFIX$"))
except Exception as e:
print_error("Failed to include build number: {}".format(e))
return False
return True
###############################################################################
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{}".format(__version__))
global project_version
global arma3tools_path
global work_drive
global module_root
global make_root
global release_dir
global module_root_parent
global optionals_root
global key_name
global key
global dssignfile
global prefix
global pbo_name_prefix
global ciBuild
global missingFiles
global failedBuilds
global printedErrors
printedErrors = 0
if sys.platform != "win32":
print_error("Non-Windows platform (Cygwin?). Please re-run from cmd.")
sys.exit(1)
reg = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER)
try:
k = winreg.OpenKey(reg, r"Software\bohemia interactive\arma 3 tools")
arma3tools_path = winreg.QueryValueEx(k, "path")[0]
winreg.CloseKey(k)
except:
raise Exception("BadTools","Arma 3 Tools are not installed correctly or the P: drive needs to be created.")
# Default behaviors
test = False # Copy to Arma 3 directory?
arg_modules = False # Only build modules on command line?
use_pboproject = True # Default to pboProject build tool
make_target = "DEFAULT" # Which section in make.cfg to use for the build
new_key = True # Make a new key and use it to sign?
quiet = False # Suppress output from build tool?
# Parse arguments
if "help" in argv or "-h" in argv or "--help" in argv:
print ("""
make.py [help] [test] [force] [key <name>] [target <name>] [release <version>]
[module name] [module name] [...]
test -- Copy result to Arma 3.
release <version> -- Make archive with <version>.
force -- Ignore cache and build all.
checkexternal -- Check External Files
target <name> -- Use rules in make.cfg under heading [<name>] rather than
default [Make]
key <name> -- Use key in working directory with <name> to sign. If it does not
exist, create key.
quiet -- Suppress command line output from build tool.
If module names are specified, only those modules will be built.
Examples:
make.py force test
Build all modules (ignoring cache) and copy the mod folder to the Arma 3
directory.
make.py mymodule_gun
Only build the module named 'mymodule_gun'.
make.py force key MyNewKey release 1.0
Build all modules (ignoring cache), sign them with NewKey, and pack them
into a zip file for release with version 1.0.
If a file called $NOBIN$ is found in the module directory, that module will not be binarized.
See the make.cfg file for additional build options.
""")
sys.exit(0)
if "force" in argv:
argv.remove("force")
force_build = True
else:
force_build = False
if "test" in argv:
test = True
argv.remove("test")
if "release" in argv:
make_release_zip = True
argv.remove("release")
else:
make_release_zip = False
if "target" in argv:
make_target = argv[argv.index("target") + 1]
argv.remove("target")
argv.remove(make_target)
force_build = True
if "key" in argv:
new_key = True
key_name = argv[argv.index("key") + 1]
argv.remove("key")
argv.remove(key_name)
if "quiet" in argv:
quiet = True
argv.remove("quiet")
if "checkexternal" in argv:
argv.remove("checkexternal")
check_external = True
else:
check_external = False
if "version" in argv:
argv.remove("version")
version_update = True
else:
version_update = False
version_increments = []
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
print_yellow("\nCheck external references is set to {}".format(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))
os.chdir(make_root)
cfg = configparser.ConfigParser();
try:
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="")
# Release archive prefix
zipPrefix = cfg.get(make_target, "zipPrefix", fallback=project.lstrip("@").lower())
# Should we autodetect modules on a complete build?
module_autodetect = cfg.getboolean(make_target, "module_autodetect", fallback=True)
# Manual list of modules to build for a complete build
modules = cfg.get(make_target, "modules", fallback=None)
# Parse it out
if modules:
modules = [x.strip() for x in modules.split(',')]
else:
modules = []
# List of directories to ignore when detecting
ignore = [x.strip() for x in cfg.get(make_target, "ignore", fallback="release").split(',')]
# Which build tool should we use?
build_tool = cfg.get(make_target, "build_tool", fallback="addonbuilder").lower()
# Release/build directory, relative to script dir
release_dir = cfg.get(make_target, "release_dir", fallback="release")
#Directory to copy the final built PBO's for a test run.
test_dir = cfg.get(make_target, "test_dir", fallback=os.path.join(os.environ["USERPROFILE"],r"documents\Arma 3"))
# Project PBO file prefix (files are renamed to prefix_name.pbo)
pbo_name_prefix = cfg.get(make_target, "pbo_name_prefix", fallback=None)
# Project module Root
module_root_parent = os.path.abspath(os.path.join(os.path.join(work_drive, prefix), os.pardir))
module_root = cfg.get(make_target, "module_root", fallback=os.path.join(make_root_parent, "addons"))
optionals_root = os.path.join(module_root_parent, "optionals")
extensions_root = os.path.join(module_root_parent, "extensions")
if (os.path.isdir(module_root)):
os.chdir(module_root)
else:
print_error ("Directory {} does not exist.".format(module_root))
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)):
print_green ("optionals_root: {}".format(optionals_root))
else:
print("optionals_root does not exist: {}".format(optionals_root))
print_green ("release_dir: {}".format(release_dir))
except:
raise
print_error("Could not parse make.cfg.")
sys.exit(1)
# See if we have been given specific modules to build from command line.
if len(argv) > 1 and not make_release_zip:
arg_modules = True
modules = [a for a in argv[1:] if a[0] != "-"]
# Find the tools we need.
try:
tools = find_bi_tools(work_drive)
addonbuilder = tools[0]
dssignfile = tools[1]
dscreatekey = tools[2]
cfgconvert = tools[3]
except:
print_error("Arma 3 Tools are not installed correctly or the P: drive has not been created.")
sys.exit(1)
if build_tool == "pboproject":
try:
depbo_tools = find_depbo_tools("HKLM")
if depbo_tools == -1:
depbo_tools = find_depbo_tools("HKCU")
pboproject = depbo_tools[0]
rapifyTool = depbo_tools[1]
makepboTool = depbo_tools[2]
except:
raise
print_error("Could not find dePBO tools. Download the needed tools from: https://dev.withsix.com/projects/mikero-pbodll/files")
sys.exit(1)
# Try to open and deserialize build cache file.
try:
cache = {}
with open(os.path.join(make_root, "make.cache"), 'r') as f:
cache_raw = f.read()
cache = json.loads(cache_raw)
except:
print ("No cache found.")
cache = {}
# Check the build version (from main) with cached version - forces a full rebuild when version changes
cacheVersion = "None";
if 'cacheVersion' in cache:
cacheVersion = cache['cacheVersion']
if (project_version != cacheVersion):
cache = {}
print("Reseting Cache {0} to New Version {1}".format(cacheVersion, project_version))
cache['cacheVersion'] = project_version
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
failedBuilds = []
missingFiles = []
# Update version stamp in all files that contain it
# Update version only for release if full update not requested (backup and restore files)
print_blue("\nChecking for obsolete version numbers...")
if not version_update:
stash_version_files_for_building()
else:
# Set version
set_version_in_files();
print("Version in files has been changed, make sure you commit and push the updates!")
try:
# Temporarily copy optionals_root for building. They will be removed later.
if (os.path.isdir(optionals_root)):
optionals_modules = []
optional_files = []
copy_optionals_for_building(optionals_modules,optional_files)
# Get list of subdirs in make root.
dirs = next(os.walk(module_root))[1]
# Autodetect what directories to build.
if module_autodetect and not arg_modules:
modules = []
for path in dirs:
# Any dir that has a config.cpp in its root is an addon to build.
config_path = os.path.join(path, 'config.cpp')
if os.path.isfile(config_path) and not path in ignore:
modules.append(path)
# Make the key specified from command line if necessary.
if new_key:
if not os.path.isfile(os.path.join(private_key_path, key_name + ".biprivatekey")):
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_green("Created: {}".format(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!")
else:
print_green("\nNOTE: Using key {}".format(os.path.join(private_key_path, key_name + ".biprivatekey")))
try:
print("Copying public key to release directory.")
try:
os.makedirs(os.path.join(module_root, release_dir, project, "keys"))
except:
pass
# Use biKeyNameAbrev to attempt to minimize problems from this BI Bug REFERENCE: http://feedback.arma3.com/view.php?id=22133
biKeyNameAbrev = key_name.split("-")[0]
shutil.copyfile(os.path.join(private_key_path, key_name + ".bikey"), os.path.join(module_root, release_dir, project, "keys", "{}.bikey".format(biKeyNameAbrev)))
except:
print_error("Could not copy key to release directory.")
raise
key = os.path.join(private_key_path, "{}.biprivatekey".format(key_name))
# Remove any obsolete files.
print_blue("\nChecking for obsolete files...")
obsolete_check_path = os.path.join(module_root, release_dir, project,"addons")
for file in os.listdir(obsolete_check_path):
if (file.endswith(".pbo") and os.path.isfile(os.path.join(obsolete_check_path,file))):
if check_for_obsolete_pbos(module_root, file):
fileName = os.path.splitext(file)[0]
print_yellow("Removing obsolete pbo => {}".format(file))
purge(obsolete_check_path, "{}\..".format(fileName), "{}.*".format(fileName))
obsolete_check_path = os.path.join(module_root, release_dir, project)
for file in os.listdir(obsolete_check_path):
if (file.endswith(".dll") and os.path.isfile(os.path.join(obsolete_check_path,file))):
if not os.path.exists(os.path.join(module_root_parent, file)):
print_yellow("Removing obsolete dll => {}".format(file))
try:
os.remove(os.path.join(obsolete_check_path,file))
except:
print_error("\nFailed to delete {}".format(os.path.join(obsolete_check_path,file)))
pass
# For each module, prep files and then build.
print_blue("\nBuilding...")
for module in modules:
print_green("\nMaking {}".format(module + "-"*max(1, (60-len(module)))))
missing = False
sigMissing = False
# Cache check
if module in cache:
old_sha = cache[module]
else:
old_sha = ""
# Hash the module
new_sha = get_directory_hash(os.path.join(module_root, module))
# Is the pbo or sig file missing?
missing = not os.path.isfile(os.path.join(release_dir, project, "addons", "{}{}.pbo".format(pbo_name_prefix,module)))
sigFile = "{}{}.pbo.{}.bisign".format(pbo_name_prefix,module,key_name)
sigMissing = not os.path.isfile(os.path.join(release_dir, project, "addons", sigFile))
if missing:
print_yellow("Missing PBO file {}{}.pbo. Building...".format(pbo_name_prefix,module))
# 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 {}".format(sigFile))
build_signature_file(os.path.join(module_root, release_dir, project, "addons", "{}{}.pbo".format(pbo_name_prefix,module)))
# Skip everything else
continue
# Only do this if the project isn't stored directly on the work drive.
# Split the path at the drive name and see if they are on the same drive (usually P:)
if os.path.splitdrive(module_root)[0] != os.path.splitdrive(work_drive)[0]:
try:
# Remove old work drive version (ignore errors)
shutil.rmtree(os.path.join(work_drive, prefix, module), True)
# Copy module to the work drive
shutil.copytree(module, os.path.join(work_drive, prefix, module))
except:
raise
print_error("Could not copy module to work drive. Does the module exist?")
input("Press Enter to continue...")
print("Resuming build...")
continue
#else:
#print("WARNING: Module is stored on work drive ({}).".format(work_drive))
try:
# Remove the old pbo, key, and log
old = os.path.join(module_root, release_dir, project, "addons", "{}{}".format(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", "{}{}".format(pbo_name_prefix,module)) + "*"
files = glob.glob(old)
for f in files:
os.remove(f)
except:
raise
print_error("Could not copy module to work drive. Does the module exist?")
input("Press Enter to continue...")
print("Resuming build...")
continue
# Build the module into a pbo
print_blue("Building: {}".format(os.path.join(work_drive, prefix, module)))
print_blue("Destination: {}".format(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"))
except:
pass
# Run build tool
build_successful = False
if build_tool == "pboproject":
try:
nobinFilePath = os.path.join(work_drive, prefix, module, "$NOBIN$")
backup_config(module)
version_stamp_pboprefix(module,commit_id)
if os.path.isfile(nobinFilePath):
print_green("$NOBIN$ Found. Proceeding with non-binarizing!")
cmd = [makepboTool, "-P","-A","-L","-G","-X=*.backup", os.path.join(work_drive, prefix, module),os.path.join(module_root, release_dir, project,"addons")]
else:
if check_external:
cmd = [pboproject, "-P", os.path.join(work_drive, prefix, module), "+Engine=Arma3", "-S","+Noisy", "+X", "+Clean", "+Mod="+os.path.join(module_root, release_dir, project), "-Key"]
else:
cmd = [pboproject, "-P", os.path.join(work_drive, prefix, module), "+Engine=Arma3", "-S","+Noisy", "-X", "+Clean", "+Mod="+os.path.join(module_root, release_dir, project), "-Key"]
color("grey")
if quiet:
devnull = open(os.devnull, 'w')
ret = subprocess.call(cmd, stdout=devnull)
devnull.close()
else:
ret = subprocess.call(cmd)
color("reset")
if ret == 0:
print_green("pboProject return code == {}".format(str(ret)))
# Prettyprefix rename the PBO if requested.
if pbo_name_prefix:
try:
os.rename(os.path.join(module_root, release_dir, project, "addons", "{}.pbo".format(module)), os.path.join(module_root, release_dir, project, "addons", "{}{}.pbo".format(pbo_name_prefix,module)))
except:
raise
print_error("Could not rename built PBO with prefix.")
# Sign result
if (key and not "{}{}.pbo".format(pbo_name_prefix,module) in signature_blacklist):
print("Signing with {}.".format(key))
if pbo_name_prefix:
ret = subprocess.call([dssignfile, key, os.path.join(module_root, release_dir, project, "addons", "{}{}.pbo".format(pbo_name_prefix,module))])
else:
ret = subprocess.call([dssignfile, key, os.path.join(module_root, release_dir, project, "addons", "{}.pbo".format(module))])
if ret == 0:
build_successful = True
else:
build_successful = True
if not build_successful:
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 ("Resuming build...")
failedBuilds.append("{}".format(module))
continue
# Back to the root
os.chdir(module_root)
except:
raise
print_error("Could not run Addon Builder.")
input("Press Enter to continue...")
print ("Resuming build...")
continue
finally:
addon_restore(os.path.join(work_drive, prefix, module))
elif build_tool== "addonbuilder":
# Detect $NOBIN$ and do not binarize if found.
if os.path.isfile(os.path.join(work_drive, prefix, module, "$NOBIN$")):
do_binarize = False
print("$NOBIN$ file found in module, packing only.")
else:
do_binarize = True
try:
# 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]
if not do_binarize:
cmd.append("-packonly")
if quiet:
previousDirectory = os.getcwd()
os.chdir(arma3tools_path)
devnull = open(os.devnull, 'w')
ret = subprocess.call(cmd, stdout=devnull)
devnull.close()
os.chdir(previousDirectory)
else:
previousDirectory = os.getcwd()
os.chdir(arma3tools_path)
print_error("Current directory - {}".format(os.getcwd()))
ret = subprocess.call(cmd)
os.chdir(previousDirectory)
print_error("Current directory - {}".format(os.getcwd()))
color("reset")
print_green("completed")
# Prettyprefix rename the PBO if requested.
if pbo_name_prefix:
try:
os.rename(os.path.join(make_root, release_dir, project, "addons", "{}.pbo".format(module)), os.path.join(make_root, release_dir, project, "addons", "{}{}.pbo".format(pbo_name_prefix,module)))
except:
raise
print_error("Could not rename built PBO with prefix.")
if ret == 0:
# Sign result
#print_yellow("Sig_fileName: ace_{}.pbo".format(module))
if (key and not "{}{}.pbo".format(pbo_name_prefix,module) in signature_blacklist) :
print("Signing with {}.".format(key))
if pbo_name_prefix:
ret = subprocess.call([dssignfile, key, os.path.join(make_root, release_dir, project, "addons","{}{}.pbo".format(pbo_name_prefix,module))])
else:
ret = subprocess.call([dssignfile, key, os.path.join(make_root, release_dir, project, "addons", "{}.pbo".format(module))])
if ret == 0:
build_successful = True
else:
build_successful = True
if not build_successful:
print_error("Module not successfully built. Check your {}temp\{}_packing.log for more info.".format(work_drive,module))
# Back to the root
os.chdir(make_root)
except:
raise
print_error("Could not run Addon Builder.")
input("Press Enter to continue...")
print ("Resuming build...")
continue
else:
print_error("Unknown build_tool {}!".format(build_tool))
# Update the hash for a successfully built module
if build_successful:
cache[module] = new_sha
except Exception as e:
print_yellow("Cancel or some error detected: {}".format(e))
finally:
copy_important_files(module_root_parent,os.path.join(release_dir, project))
if (os.path.isdir(optionals_root)):
cleanup_optionals(optionals_modules)
if not version_update:
restore_version_files()
# Done building all modules!
# Write out the cache state
cache_out = json.dumps(cache)
with open(os.path.join(make_root, "make.cache"), 'w') as f:
f.write(cache_out)
# Delete the pboproject temp files if building a release.
if make_release_zip and build_tool == "pboproject":
try:
shutil.rmtree(os.path.join(release_dir, project, "temp"), True)
except:
print_error("ERROR: Could not delete pboProject temp files.")
# Make release
if make_release_zip:
release_name = "{}_{}".format(zipPrefix, project_version.rsplit(".", 1)[0])
try:
# Delete all log files
for root, dirs, files in os.walk(os.path.join(release_dir, project, "addons")):
for currentFile in files:
if currentFile.lower().endswith("log"):
os.remove(os.path.join(root, currentFile))
# Remove all zip files from release folder to prevent zipping the zip
for file in os.listdir(release_dir):
if file.endswith(".zip"):
os.remove(os.path.join(release_dir, file))
# 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)
# Move release zip to release folder
shutil.copy(release_zip, release_dir)
os.remove(release_zip)
except:
raise
print_error("Could not make release.")
# Copy to Arma 3 folder for testing
if test:
print_blue("\nCopying to Arma 3.")
if sys.platform == "win32":
reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
try:
k = winreg.OpenKey(reg, r"SOFTWARE\Wow6432Node\Bohemia Interactive\Arma 3")
a3_path = winreg.EnumValue(k, 1)[1]
winreg.CloseKey(k)
except:
print_error("Could not find Arma 3's directory in the registry.")
else:
a3_path = cygwin_a3path
print_yellow("Path from the registry => {}".format(a3_path))
a3_path = test_dir
print_yellow("Copying build files to {}".format(a3_path))
if os.path.exists(a3_path):
try:
shutil.rmtree(os.path.join(a3_path, project), True)
shutil.copytree(os.path.join(module_root, release_dir, project), os.path.join(a3_path, project))
except:
print_error("Could not copy files. Is Arma 3 running?")
tracedErrors = len(failedBuilds) + len(missingFiles)
if printedErrors > 0: # printedErrors includes tracedErrors
printedOnlyErrors = printedErrors - tracedErrors
print()
print_error("Failed with {} errors.".format(printedErrors))
if len(failedBuilds) > 0:
for failedBuild in failedBuilds:
print("- {} build failed!".format(failedBuild))
if len(missingFiles) > 0:
for missingFile in missingFiles:
print("- {} not found!".format(missingFile))
if printedOnlyErrors > 0:
print_yellow("- {} untraced error(s)!".format(printedOnlyErrors))
else:
print_green("\nCompleted with 0 errors.")
if __name__ == "__main__":
start_time = timeit.default_timer()
main(sys.argv)
d,h,m,s = Fract_Sec(timeit.default_timer() - start_time)
print("\nTotal Program time elapsed: {0:2}h {1:2}m {2:4.5f}s".format(h,m,s))
if ciBuild:
sys.exit(0)
input("Press Enter to continue...")