update
It appears that this only really affects those using the titanium-bundle, as it adds an extra hook for the alloy process, thus causing it to fire twice. All Alloy projects contain their own plugin, so there's no need for anything Alloy-related to be in paths.hooks, otherwise we get problems like this.
paths.hooks = ["/Users/tlukasavage/Development/node_modules/titanium-bundle/node_modules/alloy/hooks"]
You can see that more than one alloy plugin is found and used in the CLI output when you execute "ti build -p ios":
[DEBUG] Loaded plugin hooks:
[DEBUG] /Users/tlukasavage/Development/node_modules/titanium-bundle/node_modules/alloy/hooks/alloy.js
[DEBUG] /Users/tlukasavage/Development/node_modules/titanium-bundle/node_modules/titanium/hooks/tisdk3fixes.js
[DEBUG] /Users/tlukasavage/Library/Application Support/Titanium/mobilesdk/osx/3.2.0/iphone/cli/hooks/install.js
[DEBUG] /Users/tlukasavage/Library/Application Support/Titanium/mobilesdk/osx/3.2.0/iphone/cli/hooks/package.js
[DEBUG] /Users/tlukasavage/Library/Application Support/Titanium/mobilesdk/osx/3.2.0/iphone/cli/hooks/run.js
[DEBUG] /Users/tlukasavage/Development/twice/plugins/ti.alloy/hooks/alloy.js
Deleting the
paths.hooks
entry "/Users/tlukasavage/Development/node_modules/titanium-bundle/node_modules/alloy/hooks" causes everything to go back to working as normal. In the end, the titanium-bundle should not be installing hooks for Alloy, and the CLI should have some means of identifying that a hook has already run for the given build.
To reproduce
ti create -p ios -n twice --id test.twice -d .
cd twice
alloy new
ti build -p ios
Analysis
If you remove the Alloy plugin from
tiapp.xml
it solves the problem for the next build, but then the compiler itself will re-add the plugin, causing the next run to compile twice again.
<plugins>
<plugin version="1.0">ti.alloy</plugin>
</plugins>
Log
The log showing the compile being done twice:
[INFO] Forcing rebuild: /Users/zandbergen/dev/tests/twice/build/iphone/build-manifest.json does not exist
[INFO] Initiating prepare phase
[INFO] Found Alloy app in /Users/zandbergen/dev/tests/twice/app
[DEBUG] ----- CONFIGURATION -----
[DEBUG] raw config = "platform=ios,version=0,simtype=none,devicefamily=universal,deploytype=development"
[DEBUG] platform = ios
[DEBUG] version = 0
[DEBUG] simtype = none
[DEBUG] devicefamily = universal
[DEBUG] deploytype = development
[DEBUG] project path = /Users/zandbergen/dev/tests/twice
[DEBUG] app path = /Users/zandbergen/dev/tests/twice/app
[DEBUG]
[DEBUG] ----- CONFIG.JSON -----
[DEBUG] {
[DEBUG] "dependencies": {},
[DEBUG] "sourcemap": true,
[DEBUG] "autoStyle": false,
[DEBUG] "adapters": [
[DEBUG] "localStorage",
[DEBUG] "properties",
[DEBUG] "sql"
[DEBUG] ]
[DEBUG] }
[DEBUG]
[DEBUG] ----- CLEANING RESOURCES -----
[DEBUG] Removing orphaned controllers...
[DEBUG] Removing orphaned models...
[DEBUG] Removing orphaned styles...
[DEBUG] Removing orphaned sync adapters...
[DEBUG] Removing orphaned assets and libs...
[DEBUG]
[DEBUG] ----- BASE RUNTIME FILES -----
[TRACE] SRC_DIR=/Users/zandbergen/node_modules/titanium-bundle/node_modules/alloy/Alloy/lib
[TRACE] Copying SRC_DIR/alloy.js --> Resources/iphone/alloy.js
[TRACE] Copying SRC_DIR/alloy/backbone.js --> Resources/iphone/alloy/backbone.js
[TRACE] Copying SRC_DIR/alloy/underscore.js --> Resources/iphone/alloy/underscore.js
[TRACE] Copying SRC_DIR/alloy/widget.js --> Resources/iphone/alloy/widget.js
[TRACE] Copying SRC_DIR/alloy/controllers/BaseController.js --> Resources/iphone/alloy/controllers/BaseController.js
[TRACE] Copying SRC_DIR/alloy/sync/localStorage.js --> Resources/iphone/alloy/sync/localStorage.js
[TRACE] Copying SRC_DIR/alloy/sync/properties.js --> Resources/iphone/alloy/sync/properties.js
[TRACE] Copying SRC_DIR/alloy/sync/sql.js --> Resources/iphone/alloy/sync/sql.js
[TRACE]
[TRACE] SRC_DIR=/Users/zandbergen/node_modules/titanium-bundle/node_modules/alloy/Alloy/common
[TRACE] Copying SRC_DIR/constants.js --> Resources/iphone/alloy/constants.js
[TRACE]
[TRACE] SRC_DIR=/Users/zandbergen/dev/tests/twice/app/assets
[TRACE]
[DEBUG]
[INFO] ----- MVC GENERATION -----
[INFO] [global style] loading from cache...
[INFO] [index.xml] view processing...
[INFO] style: "index.tss"
[INFO] view: "index.xml"
[INFO] controller: "index.js"
[TRACE] - Processing "builtins" module...
[TRACE] - Processing "optimizer" module...
[TRACE] - Processing "compress" module...
[INFO] created: "Resources/iphone/alloy/controllers/index.js"
[DEBUG] map: "build/map/Resources/iphone/alloy/controllers/index.js.map"
[INFO] created: "Resources/iphone/alloy/styles/index.js"
[INFO]
[INFO] [app.js] using cached app.js...
[INFO]
[INFO] ----- OPTIMIZING -----
[INFO] - iphone/alloy.js
[TRACE] processing "builtins" module...
[TRACE] processing "optimizer" module...
[TRACE] processing "compress" module...
[INFO] - iphone/alloy/sync/localStorage.js
[TRACE] processing "builtins" module...
[TRACE] processing "optimizer" module...
[TRACE] processing "compress" module...
[INFO] - iphone/alloy/sync/properties.js
[TRACE] processing "builtins" module...
[TRACE] processing "optimizer" module...
[TRACE] processing "compress" module...
[INFO] - iphone/alloy/sync/sql.js
[TRACE] processing "builtins" module...
[TRACE] processing "optimizer" module...
[TRACE] processing "compress" module...
[TRACE]
[TRACE] Benchmarking
[TRACE] ------------
[TRACE] [0.67619s] TOTAL
[INFO]
[INFO] Alloy compiled in 0.67619s
[INFO] Found Alloy app in /Users/zandbergen/dev/tests/twice/app
[INFO] Executing Alloy compile: /usr/local/bin/node /usr/local/bin/alloy compile /Users/zandbergen/dev/tests/twice/app --config platform=ios,version=0,simtype=none,devicefamily=universal,deploytype=development
[DEBUG] .__ .__
[DEBUG] _____ | | | | ____ ___.__.
[DEBUG] \__ \ | | | | / _ < | |
[DEBUG] / __ \| |_| |_( <_> )___ |
[DEBUG] (____ /____/____/\____// ____|
[DEBUG] \/ \/
[DEBUG] Alloy 1.3.0 by Appcelerator. The MVC app framework for Titanium.
[DEBUG]
[DEBUG] ----- CONFIGURATION -----
[DEBUG] raw config = "platform=ios,version=0,simtype=none,devicefamily=universal,deploytype=development"
[DEBUG] platform = ios
[DEBUG] version = 0
[DEBUG] simtype = none
[DEBUG] devicefamily = universal
[DEBUG] deploytype = development
[DEBUG] project path = /Users/zandbergen/dev/tests/twice
[DEBUG] app path = /Users/zandbergen/dev/tests/twice/app
[DEBUG]
[DEBUG] ----- CONFIG.JSON -----
[DEBUG] {
[DEBUG] "dependencies": {},
[DEBUG] "sourcemap": true,
[DEBUG] "autoStyle": false,
[DEBUG] "adapters": [
[DEBUG] "localStorage",
[DEBUG] "properties",
[DEBUG] "sql"
[DEBUG] ]
[DEBUG] }
[DEBUG]
[DEBUG] ----- CLEANING RESOURCES -----
[DEBUG] Removing orphaned controllers...
[DEBUG] Removing orphaned models...
[DEBUG] Removing orphaned styles...
[DEBUG] Removing orphaned sync adapters...
[DEBUG] Removing orphaned assets and libs...
[DEBUG]
[DEBUG] ----- BASE RUNTIME FILES -----
[TRACE] SRC_DIR=/Users/zandbergen/node_modules/titanium-bundle/node_modules/alloy/Alloy/lib
[TRACE] Copying SRC_DIR/alloy.js --> Resources/iphone/alloy.js
[TRACE] Copying SRC_DIR/alloy/backbone.js --> Resources/iphone/alloy/backbone.js
[TRACE] Copying SRC_DIR/alloy/underscore.js --> Resources/iphone/alloy/underscore.js
[TRACE] Copying SRC_DIR/alloy/widget.js --> Resources/iphone/alloy/widget.js
[TRACE] Copying SRC_DIR/alloy/controllers/BaseController.js --> Resources/iphone/alloy/controllers/BaseController.js
[TRACE] Copying SRC_DIR/alloy/sync/localStorage.js --> Resources/iphone/alloy/sync/localStorage.js
[TRACE] Copying SRC_DIR/alloy/sync/properties.js --> Resources/iphone/alloy/sync/properties.js
[TRACE] Copying SRC_DIR/alloy/sync/sql.js --> Resources/iphone/alloy/sync/sql.js
[TRACE]
[TRACE] SRC_DIR=/Users/zandbergen/node_modules/titanium-bundle/node_modules/alloy/Alloy/common
[TRACE] Copying SRC_DIR/constants.js --> Resources/iphone/alloy/constants.js
[TRACE]
[TRACE] SRC_DIR=/Users/zandbergen/dev/tests/twice/app/assets
[TRACE]
[DEBUG]
[INFO] ----- MVC GENERATION -----
[INFO] [global style] loading from cache...
[INFO] [index.xml] view processing...
[INFO] style: "index.tss"
[INFO] view: "index.xml"
[INFO] controller: "index.js"
[TRACE] - Processing "builtins" module...
[TRACE] - Processing "optimizer" module...
[TRACE] - Processing "compress" module...
[INFO] created: "Resources/iphone/alloy/controllers/index.js"
[DEBUG] map: "build/map/Resources/iphone/alloy/controllers/index.js.map"
[INFO] created: "Resources/iphone/alloy/styles/index.js"
[INFO]
[INFO] [app.js] using cached app.js...
[INFO]
[INFO] ----- OPTIMIZING -----
[INFO] - iphone/alloy.js
[TRACE] processing "builtins" module...
[TRACE] processing "optimizer" module...
[TRACE] processing "compress" module...
[INFO] - iphone/alloy/sync/localStorage.js
[TRACE] processing "builtins" module...
[TRACE] processing "optimizer" module...
[TRACE] processing "compress" module...
[INFO] - iphone/alloy/sync/properties.js
[TRACE] processing "builtins" module...
[TRACE] processing "optimizer" module...
[TRACE] processing "compress" module...
[INFO] - iphone/alloy/sync/sql.js
[TRACE] processing "builtins" module...
[TRACE] processing "optimizer" module...
[TRACE] processing "compress" module...
[TRACE]
[TRACE] Benchmarking
[TRACE] ------------
[TRACE] [0.55402s] TOTAL
[INFO]
[INFO] Alloy compiled in 0.55402s
[INFO] Alloy compiler completed successfully
[INFO] Cleaning old build directory
[INFO] Performing full rebuild
[INFO] Copying Xcode iOS files
[DEBUG] Copying /Users/zandbergen/Library/Application Support/Titanium/mobilesdk/osx/3.2.0.v20131122172908/iphone/Classes => /Users/zandbergen/dev/tests/twice/build/iphone/Classes
So it looks like the titanium-bundle is installing any hooks or commands it can find and them to the ti config. This is causing the Alloy hook to be fired twice. Once for the one in the project, once for the one that the titanium-bundle added. This is obviously not the desired behavior. I can hack titanium-bundle to not do this for Alloy, but a better long term solution would be for the CLI to be able to determine if 2 hooks have the same purpose, and when it has access to a global one versus a project-specific one, it should only fire the project specific one.
workaround
Execute "ti config", find the extra path.hooks entry for alloy, and delete it.As far as the CLI is concerned, a CLI plugin hook is just a JavaScript file. It has no idea what the JavaScript file is. There is no notion of a hook name/id or version. There's no way for the CLI to say "hey, you already loaded the Alloy hook" because it doesn't know what an Alloy hook is. Someday we may add a name/id and version to each individual hook, but it would be optional. Since the Alloy hook knows how to identify itself, the Alloy hook should detect if there's already an Alloy hook loaded and if so, return. This can be done by referencing
cli.hooks.loadedFilenames
or maybe scanningcli.hooks.pre\['build.pre.compile'\]
andcli.hooks.post\['build.pre.compile'\]
for your callback functions.I think this should be the job of the CLI, via the means of id and version like you stated. It seems silly to push that responsibility out every single hook, rather than just solve the problem centrally in the CLI, especially when the workarounds you suggest AFAIK are undocumented. The issue in this ticket is addressed for the most part, but the underlying problem IMO needs to be addressed by the CLI, making it a bit more sensible when executing hooks that may be installed globally and locally to the current project.
Maybe the CLI will support it someday. That doesn't solve the problem today that there are plugin hooks in the wild that would not implement these ids and versions. Technically the old plugin system had the same problem. In the meantime, add yourself to the watch list for TIMOB-13847 and maybe it'll land in 3.2.1.
/me subscribed And that's fine if there's some on the wild that don't support the different format. That would be pretty trivial in the CLI to make the distinction between a string entry, which gets no validation, and an entry that is an object containing a name, version, etc...
Closing ticket as Won't Fix.