Waf Task should use variant directory - waf

I am creating files in a Task, the example code looks as follows:
from waflib import Task, TaskGen
def build(bld):
bld(features='write_file')
class xyz(Task.Task):
def run(self):
self.generator.path.get_bld().make_node(self.outputs[0].relpath())
#TaskGen.feature('write_file')
def make_tasks(self):
for x in range(20):
src = bld.path.find_node('wscript')
tgt = src.change_ext('.'+str(x))
tsk = self.create_task('xyz', src=src, tgt=tgt)
Now all files get placed inside the build directory, but I want them to be placed in build\abc. How do I do that? For normal builds, I can use a BuildContext and specify a variant:
from waflib.Build import BuildContext
class abc(BuildContext):
variant = 'abc'
But I can't get the BuildContext working on that example, and setting variant on a Task.Task does not work.
Update
I update the example based on neuros answer:
A minimal working example with this code looks like this:
from waflib import Task, TaskGen, Configure
Configure.autoconfig = True
def configure(cnf):
cnf.path.get_src().make_node('a/wscript').write('')
def build(bld):
bld(features='write_file')
class xyz(Task.Task):
def run(self):
self.generator.path.get_bld().find_or_declare(self.outputs[0].abspath()).write('')
#TaskGen.feature('write_file')
def make_tasks(self):
srcs = bld.path.ant_glob('**/wscript', excl='build')
for src in srcs:
build_dir_of_src = src.get_bld().parent
my_sub_node = build_dir_of_src.make_node('xyz')
my_sub_node.mkdir()
tgt_basename = src.name
tgt = my_sub_node.make_node(tgt_basename)
tsk = self.create_task('xyz', src=src, tgt=tgt)
The problem is that this creates the following:
build\xyz\wscript
build\a\xyz\wscript
But I want this:
build\xyz\wscript
build\xyz\a\wscript
So I just to create the folder xyz between build and what ever the tgt is. So exactly the behavior of variant in a BuildContext.

When tasks execute, you are already in a variant build dir. To control the outputs of a task you have to use the waflib.Node class API. In your example, change_ext get the source build directory equivalent and change the extension. To insert a subdir:
# [...]
build_dir_of_src = src.get_bld().parent
my_sub_node = build_dir_of_src.make_node("my_sub_dir")
my_sub_node.mkdir()
tgt_basename = src.change_ext('.' + str(x)).name
tgt = my_sub_node.make_node(tgt_basename)
# [...]
If you want to insert a "variant style" directory, you can use bld.bldnode (untested but you see the point, use bld.bldnode):
def get_my_bld(bld, src_node):
variant_like_dirname = "xyz"
my_bld_node = bld.bldnode.make_node(variant_like_dirname)
my_bld_node.mkdir()
rp = src_node.get_bld().relpath(bld.bldnode)
my_bld_target = my_bld_node.make_node(rp)
return my_bld_tgt
# [...]
tgt = get_my_bld(bld, src)
# [...]

Related

Is there a way to change private attribute of an Aspect progammatically?

Say I have something like the following.
def _foo_aspect_impl(target, ctx):
# operations
return FooInfo(...)
foo_aspect = aspect(implementation = _foo_aspect_impl,
attr_aspects = ['deps'],
attrs = dict(
_tool = attr.Label(
# defs
),
)
)
def _foo_rule_impl(ctx):
for dep in ctx.attr.deps:
# do something with `dep[FooInfo]`
return DefaultInfo(...)
foo_rule = rule(
implementation = _foo_rule_impl,
attrs = dict(
"deps": attr.label_list(
aspects = [foo_aspect],
)
)
)
Is there a way to change the value of foo_aspect.attr._tool, either in WORKSPACE, or at the invocation of foo_rule? Former is much preferable.
The use case being that version and repository origin of _tool might change from project to project. When aspect resides in a repository shared by two projects, it does not make sense to create two branches for these two projects just for versioning of _tool.
After a lot of head scratching I found a rather complicated way of doing it.
Since the only thing that seems to be configuarable in WORKSPACE.bazel during loading phase is other workspaces / repositories, one could actually use target aliasing together with repository loading to mutiplex configuarable targets.
Here is how it works.
First, define a new repository rule new_virtual_repository, which creates repositories that does nothing but loading the BUILD.bazel and WORKSPACE.bazel files.
# repo.bzl
load("#bazel_tools//tools/build_defs/repo:utils.bzl", "workspace_and_buildfile")
def _new_virtual_repo_impl(ctx):
# Create build file
workspace_and_buildfile(ctx)
return ctx.attr
new_virtual_repository = repository_rule(
implementation = _new_virtual_repo_impl,
attrs = dict(
build_file = attr.label(allow_single_file = True),
build_file_content = attr.string(),
workspace_file = attr.label(allow_single_file = True),
workspace_file_content = attr.string(),
),
local = True,
)
Then, create an extension file config.bzl which implements a function that generates the BUILD.bazel file and load the virtual repository:
# config.bzl
load(":repo.bzl", "new_virtual_repository")
def config(tool):
build_file_content = """
alias(
name = "tool",
actual = "%s",
""" % (tool)
new_virtual_repository(
name = "config_repo",
build_file_content = build_file_content,
)
Now in the aspect specification:
# aspect.bzl
foo_aspect = aspect(
...
attrs = dict(
_tool = attr.Label("#config_repo//:tool"),
)
)
Finally, configure the actual tool in WORKSPACE.bazel:
# WORKSPACE.bazel
load("//:config.bzl", "config")
config(tool="<actual_tool_label>")

Getting a second parameter based on the first parameter in Jenkins

I have a task where my Jenkins job needs two parameters for build. The first specifies the application name and can be either QA, Dev, Prod etc and the second is a server which is dependent on the first one.
Example: If I chose the app name as QA, the second parameter should display values like QAServer1, QAServer2, QAServer3.
I'm using Active Choices Plugin (https://wiki.jenkins.io/display/JENKINS/Active+Choices+Plugin) to get this done but facing an problem in fetching the second parameter contents.
Snapshots:
For obtaining the second parameter, I've written a Groovy code which reads the respective files of the selected first parameter and gets the details.
code:
#!/usr/bin/env groovy
import hudson.model.*
def Appliname = System.getenv("APPNAME")
//println Appliname
def list1 = []
def directoryName = "C:/Users/Dev/Desktop/JSONSTest"
def fileSubStr = Appliname
def filePattern = ~/${fileSubStr}/
def directory = new File(directoryName)
def findFilenameClosure =
{
if (filePattern.matcher(it.name).find())
{
def jsoname = it.name
def jsoname1 = jsoname.reverse().take(9).reverse()
list1.add(jsoname1.substring(1,4))
String listAsString = "[\'${list1.join("', '")}\']"
println "return"+listAsString
}
}
directory.eachFileRecurse(findFilenameClosure)
The above code will print the output as return['QAServer1', 'QAServer2'] which i want to use it as input for the second parameter.
Snapshot of Second parameter:
Somehow the Groovy script is not being executed and second parameter value remains empty. How can i get this done dynamically. Am i following the right away to it. Kindly help me figure out. TIA
Would you like to try below change
From:
def findFilenameClosure =
{
if (filePattern.matcher(it.name).find())
{
def jsoname = it.name
def jsoname1 = jsoname.reverse().take(9).reverse()
list1.add(jsoname1.substring(1,4))
String listAsString = "[\'${list1.join("', '")}\']"
println "return"+listAsString
}
}
directory.eachFileRecurse(findFilenameClosure)
To:
directory.eachFileRecurse {
if (filePattern.matcher(it.name).find()) {
def jsoname = it.name
def jsoname1 = jsoname.reverse().take(9).reverse()
list1.add(jsoname1.substring(1,4))
}
}
return list1

how to get url of an object in cherrypy?

I am new to CherryPy. I am using the default dispatcher, with a URL structure similar to this:
root = Root()
root.page1 = Page1()
root.page1.apple = Apple()
root.page2 = Page2()
root.page2.orange = Orange()
Orange renders a template, in which I need a link to Apple. I could just hardcode /page1/apple/. But how can I get the URL of Apple in a DRY manner?
Can this be done with the default dispatcher in CherryPy, or is it only possible with the Routes dispatcher?
(I am coming from the Django world, where one would use reverse() for this purpose.)
You can access to the mounted apps through
cherrypy.tree.apps[mount_point].root
root is always the mounted instance to the mount point. So a reverse function would look like:
def reverse(cls):
# get link to a class type
for app_url in cherrypy.tree.apps.keys():
if isinstance(cherrypy.tree.apps[app_url].root, cls):
# NOTE: it will return with the first mount point of this class
return app_url
Please find a sample code below that uses your classes. http://localhost:8080/page4/orange/ prints out { Orange and the link to apple: : "/page3/apple" }
import cherrypy
link_to_apple_global = ''
class Orange(object):
def __init__(self):
pass
#cherrypy.expose
#cherrypy.tools.json_out()
def index(self):
return {"Orange and the link to apple: ": link_to_apple_global}
class Page2(object):
def __init__(self):
pass
#cherrypy.expose
def index(self):
return "Page2"
class Apple(object):
def __init__(self):
pass
#cherrypy.expose
def index(self):
return "Apple"
class Page1(object):
def __init__(self):
pass
#cherrypy.expose
def index(self):
return "Page1"
class Root(object):
def __init__(self):
pass
#cherrypy.expose
def index(self):
return "Root"
def reverse(cls):
#return cherrypy.tree.apps.keys()
#return dir(cherrypy.tree.apps[''].root)
#return dir(cherrypy.tree.apps['/page3/apple'].root)
# get link to apple
for app_url in cherrypy.tree.apps.keys():
if isinstance(cherrypy.tree.apps[app_url].root, cls):
# NOTE: it will return with the first instance
return app_url
root = Root()
root.page1 = Page1()
root.page1.apple = Apple()
root.page2 = Page2()
root.page2.orange = Orange()
cherrypy.tree.mount(root, '/')
# if you do not specify the mount points you can find the objects
# in cherrypy.tree.apps[''].root...
cherrypy.tree.mount(root.page1, '/page4')
cherrypy.tree.mount(root.page2, '/page3')
cherrypy.tree.mount(root.page2.orange, '/page4/orange')
cherrypy.tree.mount(root.page1.apple, '/page3/apple')
link_to_apple_global = reverse(Apple)
cherrypy.engine.start()
cherrypy.engine.block()

How to use a static library created by a custom task?

I want use waf to trigger a makefile to build an other library. For this I created the following task:
def build(bld):
def run(self):
bld_dir = self.generator.bld.path.get_bld()
src_dir = self.inputs[0].parent
tgt = self.outputs[0]
tgt_dir = bld_dir.make_node(os.path.splitext(tgt.name)[0])
cmd = 'BUILDDIR="{tgt_dir}" make config gdb=1 debug=1 cc={cc} && BUILDDIR=" {tgt_dir}" make'.format(
tgt_dir = tgt_dir.abspath(),
cc = self.env.get_flat("CC"))
self.exec_command(cmd, cwd=src_dir.abspath())
return self.exec_command(['cp', lib.abspath(), tgt.abspath()],
cwd=tgt_dir.abspath())
bld(
rule = run,
source = "Makefile",
target = 'metis',
)
How can I tell waf that the task created a static library, so that I can use "metis" in a use keyword:
bld(
features = "cxx cxxprogramm"
source = "main.cpp",
target = 'main',
use = 'metis'
)
To finally solve the problem I created a on link_task, that basicly does notthing (similar to the fake_lib in ccroot.py):
from waflib.TaskGen import feature, after_method
from waflib.Tools.ccroot import stlink_task
class custom_stlib(stlink_task):
""" Dummy link task """
pass
#feature("custom_stlib")
def custom_lib(self):
self.env['custom_stlib_PATTERN'] = 'lib%s.a'
self.link_task = self.create_task('custom_stlib', [])
self.link_task.add_target(self.target)
def build(bld):
# ...
bld(
features = "cxx custom_stlib",
target = 'metis',
after = "metis_bld",
)

Groovy dynamically invoked class and find method doesn't work?

I trying to build a dynamic query similar to:
def domain = DomainName
def ids = 1
def domainClass = "$domain" as Class
domainClass.find("from ${domain} as m where m.job = ${ids} ").id
But it's not working.
If I'm trying this, all is fine:
def domain = DomainName
def ids = 1
DomainName.find("from ${domain} as m where m.job = ${ids} ").id
How can I use dynamic domain class name with find?
The simplest way is to use the getDomainClass method:
String domainClassName = 'com.foo.bar.Person'
def ids = 1
def domainClass = grailsApplication.getDomainClass(domainClassName).clazz
domainClass.find("from $domainClassName as m where m.job = ${ids} ").id
Note that if you're trying to get a single instance by id, use get:
long id = 1234
def person = domainClass.get(id)
and if you want to get multiple instances and you have a list of ids, you can use getAll
def ids = [1,2,3,4,5]
def people = domainClass.getAll(ids)
Also it's a REALLY bad idea to use GStrings with property values embedded - Google 'SQL Injection'
For example to find a person by username:
String username = 'foo'
def person = domainClass.find(
"from $domainClassName as m where m.username=:username",
[username: username])
You should be able to do this by explicitly using the GroovyClassLoader:
def domain = "DomainName"
def c = new GroovyClassLoader().loadClass(domain)
c.find('...').id
The best way to get a Domain class dynamically is through the GrailsApplication object. Example:
import org.codehaus.groovy.grails.commons.ApplicationHolder
def domainName = "full.package.DomainName"
def domainGrailsClass = ApplicationHolder.application.getArtefact("Domain", domainName)
def domainClass = domainGrailsClass.getClazz()
domainClass.find("from ${domainGrailsClass.name} as m where m.job = ${ids}").id
You can also use Class.forName() just as you would in Java. Use the 3 parameter version and pass in the current thread context class loader:
import grails.util.GrailsNameUtils
def domainName = "full.package.DomainName"
def domainClass = Class.forName(domainName, true, Thread.currentThread().getContextClassLoader())
domainClass.find("from ${GrailsNameUtils.getShortName(domainName)} as m where m.job = ${ids}").id
Classloaders are an ugly topic in Java and JVM frameworks. In Grails, you almost always want to use the thread context classloader. But even better is to use the GrailsApplication interface and avoid the issue altogether.
use GrailsClassUtils
GrailsClassUtils.getShortName(DomainName)
to get the name of the class, so this should work... if I understood the question
def domainClassName = GrailsClassUtils.getShortName(DomainName)
def ids = 1
DomainName.find("from ${domainClassName} as m where m.job = ${ids} ").id

Resources