Passing extra arguments to Waf script - waf

I am using the Waf build system for my project. There are a few dependencies in my project, that I do not always want to be linked and compiled. I was wondering if there is a way to pass extra arguments to Waf configure and Waf install scripts that I could read in the wscript and figure out if certain executables need to be compiled or not?

I figure out how to do this. In the wscript, create a function for options. In most cases this function should already exist.
def options(opt):
opt.add_option('-f', '--flag', dest='custom_flag', default=False, action='store_true',
help='a boolean option')
Now in the configure function, you could simply check for 'custom_flag' to be true if this argument was passed.
def configure(conf)
if (conf.options.custom_flag):
#do something
else:
#do something else
Now './waf configure --flag' will set the custom_flag to True. It is also possible to pass other non-boolean type arguments

Related

Use "subst" feature in configure step

waf has the subst feature to copy files. In build context it can be used like that:
def build(bld):
bld(features='subst', source='wscript', target='wscript_copy', is_copy=True)
But I need to make use of the subst feature in the configure step:
def configure(conf):
conf(features='subst', source='wscript', target='wscript_copy', is_copy=True)
But this is not possible, as configure has no BuildContext:
TypeError: 'ConfigurationContext' object is not callable
Is there a way to make this work?
ConfigureContext is not meant to use tasks.
If you really need it, you can do the same the conf.check() method do (see waf book ยง10.4.2). It uses waflib.Configure.run_build(self, *k, **kw), which is not exactly public. See waflib.Tools.c_config for its use by check().
It seems unneeded complexity to me. If you only need to copy files to setup your workspace, use plain python.
from shutil import copyfile
def configure(conf):
copyfile('wscript', 'wscript_copy')
The build part use tasks management and tasks dependencies to build things if needed.
If you need substitution of env variables, you can code it in a waf tool to use like:
def configure(conf):
conf.load("mysubst")
conf.substitute(wscript', 'wscript_copy')

Extract info from a Groovy DSL file?

I recently switched my logback configuration file from logback.xml to logback.groovy. Using a DSL with Groovy is more versatile than XML for this sort of thing.
I need to analyse this file programmatically, like I analysed the previous XML file (any of innumerable parsing tools). I realise that this will be imperfect, as a DSL config file sits on top of an object which it configures and must be executed, so its results are inevitably dynamic, whereas an XML file is static.
If you want to include one Groovy file in another file there are solutions. This one worked for me.
But I'm struggling to find what I need from the results.
If I put a function like this in the DSL file ...
def greet(){
println "hello world"
}
... not only can I execute it (config.greet() as below), but I can also see it listed when I go
GroovyShell shell = new GroovyShell()
def config = shell.parse( logfileConfigPath.toFile() )
println "config.class.properties ${config.class.properties}"
But if I put a line like this in the DSL file...
def MY_CONSTANT = "XXX"
... I have no idea how to find it and get its value (it is absent from the confusing and copious output from config.class.properties).
PS printing out config.properties just gives this:
[class:class logback, binding:groovy.lang.Binding#564fa2b]
... and yes, I did look at config.binding.properties: there was nothing.
further thought
My question is, more broadly, about what if any tools are available for analysis of Groovy DSL configuration files. Given that such a file is pretty meaningless without the underlying object it is configuring (an object implementing org.gradle.api.Project in the case of Gradle; I don't know what class it may be in the case of logback), you would have thought there would need to be instrumentation to kind of hitch up such an object and then observe the effects of the config file in a controlled, observable way. If Groovy DSL config files are to be as versatile as their XML counterparts surely you need something along those lines? NB I have a suspicion that org.gradle.tooling.model.GradleProject or org.gradle.tooling.model.ProjectModel might serve that purpose. Unfortunately, at the current time I am unable to get GradleConnector working, as detailed here.
I presume there is nothing of this kind for logback, and at the moment I have no knowledge of its DSL or configurable object, or the latter's class or interface...
The use of def creates a local variable in the execution of the script that is not available in the binding of the script; see this. Even dropping def will not expose MY_CONSTANT in the binding because parsing the script via GroovyShell.parse() does not interpret/execute the code.
To expose MY_CONSTANT in config's binding, change def MY_CONSTANT = "XXX" to MY_CONSTANT = "XXX" and execute the config script via config.run().

Default, platform specific, Bazel flags in bazel.rc

I was wondering if its possible for platform-specific default Bazel build flags.
For example, we want to use --workspace_status_command but this must be a shell script on Linux and must point towards a batch script for Windows.
Is there a way we can write in the tools/bazel.rc file something like...
if platform=WINDOWS build: --workspace_status_command=status_command.bat
if platform=LINUX build: --workspace_status_command=status_command.sh
We could generate a .bazelrc file by having the users run a script before building, but it would be cleaner/nicer if this was not neccessary.
Yes, kind of. You can specify config-specific bazelrc entries, which you can select by passing --config=<configname>.
For example your bazelrc could look like:
build:linux --cpu=k8
build:linux --workspace_status_command=/path/to/command.sh
build:windows --cpu=x64_windows
build:windows --workspace_status_command=c:/path/to/command.bat
And you'd build like so:
bazel build --config=linux //path/to:target
or:
bazel build --config=windows //path/to:target
You have to be careful not to mix semantically conflicting --config flags (Bazel doesn't prevent you from that). Though it will work, the results may be unpredictable when the configs tinker with the same flags.
Passing --config to all commands is tricky, it depends on developers remembering to do this, or controlling the places where Bazel is called.
I think a better answer would be to teach the version control system how to produce the values, like by putting a git-bazel-stamp script on the $PATH/%PATH% so that git bazel-stamp works.
Then we need workspace_status_command to allow commands from the PATH rather than a path on disk.
Proper way to do this is to wrap your cc_library with a custom macro, and pass hardcoded flags to copts. For full reference, look at envoy_library.bzl.
In short, your steps:
Define a macro to wrap cc_library:
def my_cc_library(
name,
copts=[],
**kwargs):
cc_library(name, copts=copts + my_flags(), **kwargs)
Define my_flags() macro as following:
config_setting(
name = "windows_x86_64",
values = {"cpu": "x64_windows"},
)
config_setting(
name = "linux_k8",
values = {"cpu": "k8"},
)
def my_flags():
x64_windows_options = ["/W4"]
k8_options = ["-Wall"]
return select({
":windows_x86_64": x64_windows_options,
":linux_k8": k8_options,
"//conditions:default": [],
})
How it works:
Depending on --cpu flag value my_flags() will return different flags.
This value is resolved automatically based on a platform. On Windows, it's x64_windows, and on Linux it's k8.
Then, your macro my_cc_library will supply this flags to every target in a project.
A better way of doing this has been added since you asked--sometime in 2019.
If you add
common --enable_platform_specific_config to your .bazelrc, then --config=windows will automatically apply on windows hosts, --config=macos on mac, --config=linux on linux, etc.
You can then add lines to your .bazelrc like:
build:windows --windows-flags
build:linux --linux-flags
There is one downside, though. This works based on the host rather than the target. So if you're cross-compiling, e.g. to mobile, and want different flags there, you'll have to go with a solution like envoy's (see other answer), or (probably better) add transitions into your graph targets. (See discussion here and here. "Flagless builds" are still under development, but there are usable hacks in the meantime.) You could also use the temporary platform_mappings API.
References:
Commit that added this functionality.
Where it appears in the Bazel docs.

User name in .bazelrc

I would like to add this to my .bazelrc, but the $(whoami) doesn't expand like if it was in a shell.
startup --output_user_root=/tmp/bazel/out/$(whoami)
It produces the literal result:
/tmp/bazel/out/$(whoami)/faedb999bdce730c9c495251de1ca1a4/execroot/__main__/bazel-out/
Is there any way to do what I want: adding a name/hash to the option in the .bashrc file?
Edit: what I really want is to set the outputRoot to /tmp/bazel/out without using an environment variable and to let bazel create it's user and workspace hash directories there.
You can run Bazel from a wrapper script. In fact, that's exactly what the bazel binary is (at least on Linux): it's a wrapper script that calls bazel-real. You can edit this wrapper script if you like, or rename it to bazel.sh and write your own wrapper.
/usr/bin/bazel is a script which looks for //tools/bazel, and if it exists, calls it. Otherwise, it calls bazel-real. This lets you check Bazel into your repo, or otherwise modify how it gets called. We use that to download a specific version of bazel, extract it, and then call it.
I would recommend creating //tools/bazel, and having that do your modification. It can then either call a versioned version of bazel, or call bazel-real. That keeps your modifications local to your repo rather than global.

Optional Environment Arguments to SCons Builders

I've noticed that calls to Object and Library builders sometimes take optional arguments at the end such as
Object('hello.c', CCFLAGS='-DHELLO')
Object('goodbye.c', CCFLAGS='-DGOODBYE')
Can Object, Library and SharedLibrary all take an arbitrary set of them or are they limited to a specific set of variables? If so this should save our current very large SCons build at work some time I hope.
The C/C++ builders recognize a specific set of arguments, called Construction Variables.
These variables can either be set on the environment or when calling the builder as you do in your question. Its often easier to set them on the environment, thus making the calls to the builders simpler, and then only modify the variables when necessary.
Here is an example:
env = Environment()
# Notice that CPPPATH, CPPDEFINES, LIBS, and LIBPATH dont include the
# compiler flags -I, -D, -l, and -L respectively, SCons will add those
# in a platform independent manner
env.Append(CCFLAGS=['-g', '-O2'])
env.Append(CPPPATH=['some/include/path'])
env.Append(CPPDEFINES=['YOUR_DEFINE'])
env.Append(LIBS=['pthread'])
env.Append(LIBPATH=['some/lib/path'])
# All of these builder calls use the construction
# variables set on the environment above
env.Object('hello.c')
env.Object('goodbye.c')
env.Program('main.cc')
If you want to override a specific variable, you can do the following
env.Object('hello.c', CPPDEFINES='HELLO')
Or, if you want to append to a specific variable, with just one call, you can do the following:
env.Object('hello.c', CPPDEFINES=[env['CPPDEFINES'], 'HELLO'])
What Brady said is mostly correct.
However, you can append any (number of) Environment() variables to the end of any builder. These create an OverrideEnvironment() which is then what is used to run the builder.
If you were to change the value of CCCOM and/or any variable which feeds into the command line for running the compiler then adding those variables to builder call would also have some impact.
If you specify a variable which has no impact on the current builder or even one which is not defined anywhere in SCons or any builders you may have created SCons will not issue a warning or an error.

Resources