Parse Avoidance

CloudBees Accelerator includes a parse avoidance feature that can almost eliminate makefile parse time. By caching and reusing parse result files, CloudBees Accelerator can speed up both full builds and incremental builds.

Parse avoidance works when emulating GNU Make with clusters or local agents.

Parse avoidance uses the concept of cache "slots." When parse avoidance is enabled, eMake maintains a slot for each combination of non-ignored command line options, non-ignored environment variable assignments, and current working directory. (Ignored arguments and environment variables are listed below.) A slot can be empty or hold a previously cached result. If the appropriate slot holds an up-to-date result, parsing is avoided.

A cached result becomes obsolete if eMake detects file system changes that might have caused a different result (with the same command line options, environment variable assignments, and current working directory). Such file system changes include any file read by the parse job, which means all makefiles, all programs invoked by $(shell) during the parse (as opposed to during rule execution), the files they read, and so on.

Console Output

When a cached parse result is reused, eMake replays the file system modifications made during the parse job and any standard output or standard error that the original parse job produced. For example, if the makefile includes

VARIABLE1 := $(shell echo Text > myfile)
VARIABLE2 := $(warning Warning: updated myfile)

then when using a cached parse result, eMake creates "myfile" and prints "Warning: updated myfile".

Enabling Parse Avoidance

You must first run a "learning" build with the parse avoidance feature enabled. To enable parse avoidance, set the following: --emake-parse-avoidance=1

For the learning build, (because the parse cache is empty), the argument only saves a new result to the cache. For subsequent builds, the argument enables the reuse of cached parse results and saves a new result to the cache as appropriate. If you do not specify --emake-parse-avoidance=1, then the parse avoidance cache is not accessed at all.

You should also disable generated dependencies (either by modifying your makefiles or by using --emake-suppress-include ; see below)

The following table describes parse avoidance-related options.

eMake OptionDescription

--emake-assetdir=< path >

Use the specified directory for cached parse results. The default directory is named .emake. (This option also determines the location of the saved dependency information for the dependency optimization feature and the cache location for JobCache.)

--emake-parse-avoidance= < 0/1 >

Avoid parsing makefiles when prior parse results remain up-to-date and cache new parse results when appropriate.

--emake-parse-avoidance-ignore-env=< var >

Ignore the named environment variable when searching for applicable cached parse results. To ignore more than one variable, use this option multiple times.

--emake-parse-avoidance-ignore-path=< path >

Ignore this file or directory when checking whether cached parse results are up-to-date. Append % for prefix matching (the % must be the last character). To ignore more than one path or prefix, use this option multiple times.

Incorrect placement of % will result in an error.

Correct: --emake-parse-avoidance-ignore-path=.foo%

Incorrect: --emake-parse-avoidance-ignore-path=.%foo

--emake-suppress-include= < pattern >

Skip matching makefile includes (such as generated dependencies). Generally, you should not suppress makefile includes unless they are generated dependency files, and you have enabled automatic dependencies as an alternative way of handling dependencies.

If the pattern does not have a directory separator, then the pattern is compared to the include’s file name component only. If the pattern has a directory separator, then the pattern is taken relative to the same working directory that applies to the include directive and compared to the included file’s entire path.

If a file is read during a parse but changes before eMake attempts to reuse that parse’s results, the cached parse result is normally considered to be obsolete. You can, however, temporarily override this decision with --emake-parse-avoidance-ignore-path.

eMake permanently ignores the effect on a given parse result of certain special files that might have existed when it was created, in all cases using the names they would have had at that time:

  • Anything in ".emake" or whatever alternative directory you specified using --emake-assetdir

  • Client-side eMake debug log (as specified by --emake-logfile )

  • Annotation file (as specified by --emake-annofile )

  • History file (as specified by --emake-historyfile )

  • Your makefiles should not contain $(shell) expansions that modify these four special files and directories in ways that affect the builds, because parse avoidance will ignore changes to them.

  • Enabling remote parse debugging (the --emake-rdebug option) disables parse avoidance.

You can use a pragma to instruct parse avoidance to detect the dependence of a parse result upon path wildcard match results. This pragma makes the parse cache sensitive to the existence (or nonexistence) of specific files in directories that it reads as well as in new subdirectories thereof. (eMake does not detect matching files in just any new subdirectory, but only those that are subdirectories of directories read in the original job and their subdirectories, recursively.)

You can specify more than one glob pattern, as in the following pragma: #pragma jobcache parse -readdir *.h -readdir *.c

For Android-specific information about parse avoidance and other Android best practices, see KB article KBEA-00130.

Parse Avoidance Example

noautodep.mk is used for this example. The contents are:

#pragma noautodep */.git/*
$(local-intermediates-dir)/libbcc-stamp.c :
#pragma noautodep */out/target/product/generic/system/bin/cat
$(linked_module) :

The following parse avoidance example provides command line arguments for Android 4.1.1.

You might need additional options such as ` --emake-cm`.
--emake-parse-avoidance=1 --emake-autodepend=1 --emake-suppress-include=*.d --emake-suppress-include=*.P --emake-debug=P -f Makefile -f noautodep.mk

Deleting the Cache

To delete the cache, delete <assetdir>/cache.*. (The default asset directory is .emake.)

For example: rm -r .emake/cache.*

Moving Your Workspace

If you want to move your workspace, make sure that the new eMake roots correspond to the old eMake roots. Also, because the asset directory defaults to .emake in the current working directory, you must either copy that directory to the new workspace or use --emake-assetdir= to specify an asset directory that you want the two workspaces to share. If you already use --emake-assetdir= to point to an asset directory within your old workspace and also want to move the asset directory, you must update its value to point to the new asset directory location.

eMake looks for eMake roots in the values of Make variables. When eMake replays a cached parse result in a new workspace, it replaces the old eMake roots in the values of those variables with the new eMake roots. This policy works well as long as no confusion exists between eMake roots and other text in those variable values. For example, if the value of a Make variable is -I/we will look into this, your old eMake root is /we, and your new eMake root is /wg, then the new value will be -I/wg will look into this. For best results, choose distinctive directory names for your workspaces.

Limitations

  • Windows is currently not supported.

  • The parse avoidance feature does not detect the need to reparse in these cases:

  • Changes to input files and programs that are used during the parse (such as makefile includes) but are not virtualized (because they are not located under --emake-root=…​ ).

  • Changes to non file system, non-registry aspects of the environment, for example, the current time of day

  • If a parse job checks the existence or timestamp of a file without reading it, parse avoidance might not invalidate its cached result when the file is created, destroyed, or modified.

  • Using --emake-autodepend=1 and --emake-suppress-include=<pattern> in conjunction with parse avoidance helps to avoid this limitation. If a generated dependency file did not exist when a parse result was saved into the cache, eMake might reuse that cached parse result even after that dependency file was created. Of course, you also benefit from eDepend’s performance and reliability gains.

  • This limitation can be mitigated by the use of #pragma jobcache parse -readdir …​, which reruns the parse job after files are created or deleted that match the pattern and are located in a directory that was subject to a wildcard match or $(shell find …​) when the cache was populated.

Troubleshooting

Enabling Parse Avoidance Debug Logging

To help troubleshoot performance issues, you should enable parse avoidance debug logging. To do so, include a capital letter “P” in the argument of the --emake-debug=<arguments> option. CloudBees engineering and support staff use the eMake debug logs to help troubleshoot problems. For more information about debug logging and log levels, see the “ eMake Debug Log Levels ” section in Chapter 11, Troubleshooting.

Debug Log Example

You can look in "P" debug logging for an explanation of why a cached result was found to be obsolete:

WK01: 0.062173 Input changed: job:J08345218 slot:b6189634a793fee2fe1929fbf47cc4e4 path:/home/aeolus/t/Makefile thenSize:175 nowSize:176 ignore:0
WK01: 0.062228 Input changed: job:J08345218 slot:b6189634a793fee2fe1929fbf47cc4e4 path:/home/aeolus/t/fog.mk thenSize:0 nowSize:1 ignore:0
...
WK01: 0.062284 Cache slot is obsolete: job:J08345218 slot:b6189634a793fee2fe1929fbf47cc4e4

Notice that " Makefile " and " fog.mk " were both bigger; either of those changes would have triggered a reparse. The "ignore:0" comments indicate that --emake-parse-avoidance-ignore-path was not used to ignore the changes.

Using Key Files for Debugging

To discover why a new cache slot was used, look in "P" debug logging for the name of the "key" file for that new cache slot; for example:

WK01: 11.853035 Saved slot definition: .emake/cache.11/i686_Linux/d9/0f/79/4fb975e8ff94dfa2569a303e62.new.2NPR8J/key
The ".new.2NPR8J" portion of the slot directory name should have already been removed automatically by eMake before you need to access the key file.

Then diff that key file against other key files in sibling directories to see what parameters changed. To determine which slot directories to compare, grep for the parse job IDs in "P" debug logging. You can get parse job IDs from annotation using CloudBees Accelerator Insight. Each key file contains an artificial cd command to express the working directory, all non-ignored environment variables, and the non-ignored command-line arguments. Ignored command-line arguments and environment variables are omitted from key files. The key file is stored in a directory whose path name is derived from the md5sum of the file itself. Also, the command line might have been normalized to one that is equivalent to the original one. eMake quotes special characters according to UNIX, Linux, and Cygwin sh shell rules.

Ignored emake Command-Line Options

The following emake command-line options do not affect which cache slot is chosen:

--emake-annodetail
--emake-annofile
--emake-annoupload
--emake-assetdir
--emake-big-file-size
--emake-build-label
--emake-class
--emake-cluster-timeout
--emake-cm
--emake-debug
--emake-hide-warning
--emake-history
--emake-history-force
--emake-historyfile
--emake-idle-time
--emake-job-limit
--emake-ledger
--emake-ledgerfile
--emake-localagents
--emake-logfile
--emake-logfile-mode
--emake-maxagents
--emake-maxlocalagentsREMOVED IN 9.1 AS PER https://cloudbees.atlassian.net/browse/EC-12154.
--emake-mem-limit
--emake-parse-avoidance-ignore-path
--emake-pedantic
--emake-priority
--emake-readdir-conflicts
--emake-resource
--emake-showinfo
--emake-tmpdir
--emake-yield-localagentsREMOVED IN 9.1 AS PER https://cloudbees.atlassian.net/browse/EC-12154.

Ignored Environment Variables

By default, the following environment variables do not affect which cache slot is chosen. You can specify additional environment variables with --emake-parse-avoidance-ignore-env. You must use --emake-parse-avoidance-ignore-env once for each variable to ignore.

_ (underscore)
ANDROID_BUILD_TOP*
ANDROID_HOST_OUT*
BUILD_DISPLAY_NAME
BUILD_ID
BUILD_NUMBER
BUILD_TIMESTAMP
BUILD_TAG
CI_JENKINS_BUILD_ID
CI_JENKINS_BUILD_NUMBER
CI_JENKINS_BUILD_TAG
CI_JENKINS_HUDSON_SERVER_COOKIE
CI_JENKINS_JENKINS_SERVER_COOKIE
COLORFGBG
COLUMNS
COMMANDER_HOME
COMMANDER_HTTPS_PORT
COMMANDER_JOBID
COMMANDER_JOBSTEPID
COMMANDER_PLUGINS
COMMANDER_PORT
COMMANDER_RESOURCENAME
COMMANDER_SERVER
COMMANDER_SESSIONID
COMMANDER_WORKSPACE
COMMANDER_WORKSPACE_NAME
COMMANDER_WORKSPACE_UNIX
COMMANDER_WORKSPACE_WINDRIVE
COMMANDER_WORKSPACE_WINUNC
DBUS_SESSION_BUS_ADDRESS
DESKTOP_SESSION
DISPLAY
ECLOUD_AGENT_NUM
ECLOUD_BUILD_CLASS
ECLOUD_BUILD_COUNTER
ECLOUD_BUILD_ID
ECLOUD_BUILD_TAG
ECLOUD_BUILD_TYPE
ECLOUD_RECURSIVE_COMMAND_FILE
EMAKE_APP_VERSION
EMAKE_BUILD_MODE
EMAKE_DEBUG
EMAKE_LEDGER_CONTEXT
EMAKE_LICENSE_FILE
EMAKE_MAKE_ID
EMAKE_PARSEFILE
EMAKE_RLOGFILE
EMAKE_ROOT
GPG_AGENT_INFO
HOSTNAME
HUDSON_COOKIE
HUDSON_HOME
HUDSON_SERVER_COOKIE
HUDSON_URL
JACK_CLIENT_SETTING*
JENKINS_COOKIE
JENKINS_HOME
JENKINS_SERVER_COOKIE
JENKINS_URL
JOB_BASE_NAME
JOB_DISPLAY_URL
JOB_NAME
KDE_FULL_SESSION
KDE_MULTIHEAD
KDE_SESSION_UID
KDE_SESSION_VERSION
KONSOLE_DBUS_SERVICE
KONSOLE_DBUS_SESSION
LINES
LS_COLORS
OLDPWD
PROMPT_COMMAND
PS1
RUN_CHANGES_DISPLAY_URL
RUN_DISPLAY_URL
SESSION_MANAGER
SHELL_SESSION_ID
SHLVL
SSH_AGENT_PID
SSH_AUTH_SOCK
SSH_CLIENT
SSH_CONNECTION
SSH_TTY
TEMP
TERM
TERMCAP
TMP
TMPDIR
WINDOWID
WINDOWPATH
WRAPPER_ARCH
WRAPPER_BIN_DIR
WRAPPER_BITS
WRAPPER_CONF_DIR
WRAPPER_DESCRIPTION
WRAPPER_DISPLAYNAME
WRAPPER_EVENT_NAME
WRAPPER_EVENT_WRAPPER_PID
WRAPPER_FILE_SEPARATOR
WRAPPER_HOSTNAME
WRAPPER_INIT_DIR
WRAPPER_JAVA_HOME
WRAPPER_EVENT_JVM_ID
WRAPPER_EVENT_JVM_PID
WRAPPER_LANG
WRAPPER_NAME
WRAPPER_OS
WRAPPER_PATH_SEPARATOR
WRAPPER_PID
WRAPPER_SYSMEM_PP.P
WRAPPER_WORKING_DIR
XCURSOR_THEME
XDG_SESSION_COOKIE
XDM_MANAGED
BUILD_DISPLAY_NAME
BUILD_ID
BUILD_NUMBER
BUILD_TAG
CI_JENKINS_BUILD_ID
CI_JENKINS_BUILD_NUMBER
CI_JENKINS_BUILD_TAG
CI_JENKINS_HUDSON_SERVER_COOKIE
CI_JENKINS_JENKINS_SERVER_COOKIE
COMMANDER_JOBID
COMMANDER_JOBSTEPID
COMMANDER_SESSIONID
COMMANDER_WORKSPACE
COMMANDER_WORKSPACE_UNIX
COMMANDER_WORKSPACE_WINDRIVE
COMMANDER_WORKSPACE_WINUNC
COMMANDER_RESOURCENAME
EMAKE_PARSEFILE
EMAKE_RLOGFILE
ECLOUD_AGENT_NUM
ECLOUD_BUILD_CLASS
ECLOUD_BUILD_COUNTER
ECLOUD_BUILD_ID
ECLOUD_BUILD_TAG
ECLOUD_BUILD_TYPE
EMAKE_APP_VERSION
EMAKE_BUILD_MODE
ECLOUD_RECURSIVE_COMMAND_FILE
EMAKE_LEDGER_CONTEXT
EMAKE_MAKE_ID
TMP
TEMP
TMPDIR
_
HOSTNAME
OLDPWD
SHLVL
SSH_AGENT_PID
SSH_AUTH_SOCK
SSH_CLIENT
SSH_CONNECTION
SSH_TTY
GPG_AGENT_INFO
COLUMNS
LINES
TERM
TERMCAP
COLORFGBG
LS_COLORS
DISPLAY
DESKTOP_SESSION
SHELL_SESSION_ID
SESSION_MANAGER
DBUS_SESSION_BUS_ADDRESS
KDE_FULL_SESSION
KDE_MULTIHEAD
KDE_SESSION_UID
KDE_SESSION_VERSION
KONSOLE_DBUS_SERVICE
KONSOLE_DBUS_SESSION
WINDOWID
WINDOWPATH
XCURSOR_THEME
XDG_SESSION_COOKIE
XDM_MANAGED
EMAKE_PARSEFILE
EMAKE_RLOGFILE
ECLOUD_AGENT_NUM
ECLOUD_BUILD_CLASS
ECLOUD_BUILD_COUNTER
ECLOUD_BUILD_ID
ECLOUD_BUILD_TAG
ECLOUD_BUILD_TYPE
EMAKE_APP_VERSION
EMAKE_BUILD_MODE
ECLOUD_RECURSIVE_COMMAND_FILE
EMAKE_LEDGER_CONTEXT
EMAKE_MAKE_ID
TMP
TEMP
TMPDIR
_
OLDPWD
SHLVL
SSH_AGENT_PID
SSH_AUTH_SOCK
SSH_CLIENT
SSH_CONNECTION
SSH_TTY
EMAKE_PARSEFILE
EMAKE_RLOGFILE
ECLOUD_AGENT_NUM
ECLOUD_BUILD_CLASS
ECLOUD_BUILD_COUNTER
ECLOUD_BUILD_ID
ECLOUD_BUILD_TAG
ECLOUD_BUILD_TYPE
EMAKE_APP_VERSION
EMAKE_BUILD_MODE
ECLOUD_RECURSIVE_COMMAND_FILE
WRAPPER_ARCH
WRAPPER_BIN_DIR
WRAPPER_BITS
WRAPPER_CONF_DIR
WRAPPER_FILE_SEPARATOR
WRAPPER_HOSTNAME
WRAPPER_INIT_DIR
WRAPPER_JAVA_HOME
WRAPPER_LANG
WRAPPER_OS
WRAPPER_PATH_SEPARATOR
GPG_AGENT_INFO
COLUMNS
LINES
TERM
TERMCAP
COLORFGBG
LS_COLORS
DISPLAY
DESKTOP_SESSION
SHELL_SESSION_ID
SESSION_MANAGER
DBUS_SESSION_BUS_ADDRESS
KDE_FULL_SESSION
KDE_MULTIHEAD
KDE_SESSION_UID
KDE_SESSION_VERSION
KONSOLE_DBUS_SERVICE
KONSOLE_DBUS_SESSION
WINDOWID
WINDOWPATH
XCURSOR_THEME
XDG_SESSION_COOKIE
XDM_MANAGED
WRAPPER_PID
WRAPPER_WORKING_DIR
WRAPPER_SYSMEM_PP.P
WRAPPER_NAME
WRAPPER_DISPLAYNAME
WRAPPER_DESCRIPTION
WRAPPER_EVENT_JVM_ID
WRAPPER_EVENT_JVM_PID
WRAPPER_EVENT_NAME
WRAPPER_EVENT_WRAPPER_PID

Ignored Android Environment Variables

The following default set of environment variables do not affect which cache slot is chosen. This list is included as a default set only when you use --emake-android-version (otherwise, you must specify them explicitly).

ANDROID_BUILD_TOP
ANDROID_HOST_OUT
JACK_CLIENT_SETTING