Notes from using xmake in a custom RTOS project #2831
Replies: 1 comment 1 reply
-
Sorry, English is not my first language and I usually have to use google translate to understand your feedback, including updates to the documentation. As a result the documentation is not perfect in many places. Also, I would like you to break up the discussion into individual topics so that I can understand and respond better. The text is too long for me to understand and there is a risk of missing some points.
The dependencies between the rules are simply binding a batch of rules and their on_xx/after_xx scripts are all executed at the same time. However, in the dev version I have added support for controlling the order of dependencies. see #2817 xmake/xmake/rules/qt/moc/xmake.lua Line 23 in 4a6e3f2
It should work and if there is a problem I need reproducible examples to be able to analyse the cause of the problem in detail.
you can use |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I've spent some time over the last couple of weeks trying to move our RTOS from CMake to xmake. This was motivated by a couple of things, largely:
I've now got a working proof-of-concept (thanks to @waruqi for answering so many questions!) that we can evaluate. Consumers who want to build a firmware image can write something like this:
As you can see, there are a few cosmetic issues, but everything basically works. I have a few notes on my experience using xmake:
The good
There were quite a few positive things from working with xmake.
buildcmds
monad is really flexible and powerful.The bad
I am going to spend a lot more time on the bad, because the good things are already good and xmake is already sufficiently good that I'd like to see it improve.
Documentation
The docs are the first thing that anyone coming to xmake encounters and they're really lacking. Almost everything that I'm using in xmake, I learned about either by asking questions here or by reading the code. This makes me pretty nervous about asking users of our system to use xmake: if they need anything that we're not providing then they're going to have problems.
The first omission is a high-level abstract model. I believe, from reading the code, that xmake has a map-fold model, where each target defines a set of inputs, a map operation that's applied to each input (usually a compile step, can be the identity function), a fold operation that combines the outputs of the map into a single output (usually a link step. This can be omitted sometimes? Object targets omit it, as do phony ones, but I don't know how general this is). What lives in .xmake? What lives in build? What lives in /tmp/? What do I need to
rm -rf
to guarantee a completely clean build? How are dependencies calculated? What are implicit arcs in the dependency graph and when (and how) do I need to add them?The
batchcmds
monad (which I really like) has no documentation. A few examples use it, but there's no docs section explaining what it is, how it works, why it exists, and so on. Even in the code, most of the methods have a single sentence fragment as a comment and I don't have enough context to know what they mean.Most of the description-scope things have equivalents in script scope, but this is not documented. These are all exposed in the code via three layers of indirection, so figuring out exactly what is happening here is not easy from the code until you've spent a day or two getting familiar with the internals of xmake.
The fact that I can define functions at description scope was not something I learned from the docs, but was very useful. I didn't learn how to define a function in a file that I can use in script scope in multiple rules / targets.
Many of the script-scope things behave differently depending on the type of thing that they're called with and this is not documented. A few them take extra parameters and these are also not documented. For example, I have no idea how I learned that I could pass
table.join(opt, {stdout = {path to output})
as the third argument tobatchcmds:vrunv
but it wasn't the docs and it's really useful.Building from object directories is flaky.
xmake likes me to run the build commands from my source directory. There are good reasons why other modern build systems have moved away from that and have strong separation between source and build paths:
Driving xmake from the object directory is really flaky. Even using the dev release, it periodically forgets that I don't want to put things in the source tree. Dependency resolution seems to sometimes break in exciting ways. To be honest, this is my biggest reservation about xmake: it promotes an antipattern and seems to be very poorly tested in cases where people try to avoid it.
Some things are just strings, some things are objects
set_kind
just sets a string. You can set this to anything, but the codebase is then scattered with things that will fail because they'll compare the string against some known values and not find anything. If this is necessary at all (I'm not convinced that it is,add_rule("binary")
ought to be able to express anythingset_kind("binary")
can), it would be great if there were an API for adding new kinds of target and all of the things that doif target:get("kind") == "whatever
instead called methods on the object returned fromtarget:kind()
. Some of these may be querying properties of the kind (e.g.target:kind().is_linker_input()
).There were a few other places with this kind of pattern. CMake has a two-tier system of built-in things that are completely immutable, other things are in the script. For the most part, xmake lacks this and it's fantastic that I can extend it in almost any place, so exceptions to that make me sad. Anything like
set_kind
adds a point where users can't hook into the code and extend it for their uses.Some things are easy to use from script scope, some are not
Our firmware rule needs to construct a linker script based on the libraries and compartments that the firmware target depends on.
add_configfiles
is easy to do in description scope, I have no idea how to do it in script scope. Fortunately, I could work around that because users will call thefirmware()
function that I define at description scope and that will add the config file. I then set the config vars in script scope in my rule (once we've collected all of the dependencies and can inspect them). I learned how to do this by reading the code for configfiles. This means that I'm relying on an implementation detail, which makes me nervous.Creating a new target from script scope is not supported. Modifying a target other than the one that the rule is processing works (and I need to do it) but is apparently not recommended.
Composition is not well defined
I learned that a rule can depend on another rule. It's still not clear to me exactly how this composition works. This feels like a form of inheritance or delegation but there are several kinds of things that I'd want to do there:
on_*
/after_*
hooks run before the ones of the rule that I depend on.I believe that
add_depends
makes mine run after the rule that I depend on. I don't know how to do the other forms of composition.Dependency calculation seems to not always work
I hit two bugs in dependency calculation that are either bugs in my
xmake.lua
or in xmake itself. The fact that I can't identify the root cause of either makes me nervous:.in
file for my linker script, it is not regenerated. I have added the output file as an explicit dependency of my link job withbatchcmds:add_depfiles
, but the dependency of the output on the .in file seems to be lost. The configfile thing does seem to be rerun ifxmake.lua
changes.Paths are treated as strings
The library provides some nice path-manipulation utilities, but all of the APIs treat paths as strings. This was one of the best features of Fabrique: it had a type for files and expanded them to strings only when invoking other tools. This made it possible to differentiate between arguments that were file references and ones that weren't, which meant that you could do things like set up sandboxing policies for tools based on the files that they were supposed to be able to access. It also meant that string operations on paths just didn't work, so you didn't end up in the mess CMake found itself where so many people write
"${foo}/${bar}"
that you need to handle "/" as a path separator on Windows.It may be too late to fix this without it being a breaking change, but I'd love to see xmake add an explicit file type that things like
runv
could handle as arguments and convert to strings, but which were never implicitly converted to strings elsewhere.Beta Was this translation helpful? Give feedback.
All reactions