How to create a Makefile or .pro file that runs a custom build event - linux

I currently use a small program to process Qt form (.ui) files and automatically generate classes which have a common base class and use virtual functions to access the form elements.
On windows, I run this tool as a custom build step on the ui form file. The only argument to the tool is the input filename.
To clarify, on Windows, Qt runs uic on the .ui file, creating a ui_filename.h file. I need to run my tool on that file.
How can/should I do this on linux? Ideally I'd build it into the .pro file, but I'm happy to edit the Makefile as well.
I'm not awesome at writing Makefiles so this may be very simple. I am happy to write the command manually for each ui_ or *.ui file but ideally it would happen automatically for all .ui files.

It is not needed to write Makefiles manually. Makefiles that call custom external tool can be generated by qmake from the project file .pro.
It is needed to create a custom target using QMAKE_EXTRA_TARGETS. Then the main target should be set as denendent on that custom target (custom target name should be added to PRE_TARGETDEPS), for example How to modify the PATH variable in Qt Creator's project file (.pro)
The tool should run after generation of the form headers, so the custom target should depend on that file customtarget1.depends = ui_mainwindow.h:
customtarget1.target = form_scanner
customtarget1.commands = tool_win_bat_or_linux_shell.sh
customtarget1.depends = ui_mainwindow.h
QMAKE_EXTRA_TARGETS += customtarget1
PRE_TARGETDEPS += form_scanner
The above qmake commands create the following Makefile rules:
# the form header depends on mainwindow.ui
ui_mainwindow.h: ..\test\mainwindow.ui
<tab>#build command...
# form scanner depends on ui_mainwindow.h
form_scanner: ui_mainwindow.h
<tab>tool_win_bat_or_linux_shell.sh
# the final target depends on form scanner
$(DESTDIR_TARGET): form_scanner ui_mainwindowm.h $(OBJECTS)
If there are many forms it is possible to create many custom targets or create one target that depends on all form files:
for (form, FORMS) {
# autogenerated form headers are located in root of build directory
FILE_NAME = $$basename(form)
# prepend ui_ and replace ending .ui by .h
FORM_HEADERS += ui_$$replace(FILE_NAME, .ui$, .h)
}
customtarget1.target = form_scanner
customtarget1.commands = tool_win_bat_or_linux_shell.sh
customtarget1.depends = $$FORM_HEADERS
QMAKE_EXTRA_TARGETS += customtarget1
PRE_TARGETDEPS += form_scanner
So, the command tool_win_bat_or_linux_shell.sh is executed only when all form headers are generated.
It is also possible to run the shell script from the project directory $$PWD and pass as command line arguments the form header file names:
customtarget1.commands = $$PWD/tool_win_bat_or_linux_shell.sh $$FORM_HEADERS
Now that shell script can run some command for each form header tool_win_bat_or_linux_shell.sh:
# for each command line argument
for file in "$#"
do
echo "$file"
ls -l $file
done

Related

How to overwrite linux system files into the yocto filesystem?

I am new, yocto build at imx6q embedded system.
I want to overwrite linux system files after do_rootfs. For example, target system files are below.
/etc/network/interface
/etc/issue
/etc/init.d/rcS
/home/root/mytest.sh
so, i made custom layer and custom recipe.
helloworld binary is copy ok.
but, do_mytask function is not called.
what's wrong with my code?
or any other method for my purpose.
#
# This file was derived from the 'Hello World!' example recipe in the
# Yocto Project Development Manual.
#
SUMMARY = "Simple helloworld application"
SECTION = "examples"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://helloworld.c"
S = "${WORKDIR}"
do_compile() {
${CC} helloworld.c -o helloworld
}
do_install() {
install -d ${D}${bindir}
install -m 0755 helloworld ${D}${bindir}
}
FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
SRC_URI += " \
file://interfaces \
file://issue \
file://mytest.sh \
"
addtask mytask after do_rootfs before do_image
do_mytask() {
install -d ${D}/etc/network
cp -af ${WORKDIR}/interfaces ${D}/etc/network/interfaces
cp -af ${WORKDIR}/issue ${D}/etc/issue
}
You'll need to extend the recipes that provide the files you want to replace.
Using /etc/network/interfaces as an example, the first step is to figure out which recipe installs that file.
From the bitbake prompt:
$ oe-pkgdata-util find-path /etc/network/interfaces
init-ifupdown: /etc/network/interfaces
So this tells us that /etc/network/interfaces is installed by the init-ifupdown receipe.
A file search shows that init-ifupdown is part of poky:
$ find . -name init-ifupdown*.bb
./poky/meta/recipes-core/init-ifupdown/init-ifupdown_1.0.bb
Now, since you need to modify the output of init-ifupdown, you'll need to extend init-ifupdown by creating a similarly named .bbappend in your own layer.
You might create the new .bbappend at
my-layer/receipes-core/init-ifupdown/init-ifupdown_%.bbappend
The % is a wildcard that ensures the .bbappend will apply to all future versions of the init-ifupdown recipe, which is probably what you want.
Place your custom interfaces file in a folder below the .bbappend:
my-layer/receipes-core/init-ifupdown/files/interfaces
The .bbappend then only needs to contain a single line to enable bitbake to pick up the new interfaces file:
FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
Finally, repeat the above with each system file you'd like to replace.
It depends on the file to modify. For example, if you search 'interfaces' in poky directories, you'll find it in 'meta/recipes-core/init-ifupdown/init-ifupdown-${PV}/'. You just need to create a recipe named init-ifupdown-${PV}.bbappend in your meta, recreating the path seen in poky (recipes-core/init-ifupdown/). This recipe can contain a single line :
FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
Then you create a 'files' folder with the 'interfaces' file you want to have.
For 'issue', like others found in the /etc directory (profile, fstab, ...), it's the same procedure, with the sources in poky/meta/recipes-core/base-files/.
For init.d scripts, use the 'update-rc' class.
You recipe is not "image recipe" (and it shouldn't be for hello world) thus you cannot use tasks do_rootfs and do_image in this case. A bit of clarification: image recipe is .bb file that you use to build image with bitbake or devtool (in your case some containing imx6q, you can find them with bitbake-layers show-recipes "*-image-*").
It looks like you are looking really is a way to override do_install of some recipe that installs that mentioned files. Then find what recipe installs those files and create bbappend file in your top layer. This bbappend file may contain do_install_append task where you can place your install <file> <dir> lines (note, using cp as not recommended, everything should be done with install tool).
Adding an extra comment based on Carsten Hansen original answer for folks working with Xilinx/Petalinux.
Under Petalinux environment we don't really have the command: oe-pkgdata-util, so the strategy is to do a search in the Xilinx SDK folder. You might have it installed on Linux under /opt according to the documentation. If you do a:
grep -r syslog-startup.conf .
you will see busybox recipe being the one that does the installation of the syslog-startup.conf.
You can create the override recipe called busybox_%.bbappend under:
../project-spec/meta-user/recipes-core/busybox/
Put the modified syslog-startup.conf file under:
../project-spec/meta-user/recipes-core/busybox/files/syslog-startup.conf
Rebuild via petalinux-build. You can also force the creation of the rootfs via petalinux-build -c rootfs and the system should populate your new file.

CMake recursively add all source files inside all subdirectories of a directory to the executable?

I have a pretty big file structure of a project which I need to convert into a multiplatform cmake project. Now it seams that cmake requires ever single cpp file be added individually to the executable. But is there a script that automates this? That snoopes through the file structure and just adds every source file automatically? Since the project will probably get a lot more source files and I probably wont be able to manually add every single one.
You could use execute_process() with a cmake -P script that uses globbing to recursively scan for source files which writes to an included file in your CMakeLists.txt i.e. something like:
"CMakeLists.txt":
execute_process(COMMAND ${CMAKE_COMMAND}
-D "RDIR=${CMAKE_CURRENT_SOURCE_DIR}"
-P "scansources.cmake"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
include("sources.cmake")
add_executable(myexe ${sources})
"scansources.cmake" (generates "sources.cmake"):
file(GLOB_RECURSE sourcelist
*.c
*.cc
*.cpp
*.cxx)
string(REGEX REPLACE "${RDIR}/" "" relative_sources "${sourcelist}")
string(REPLACE ";" "\n" sources_string "${relative_sources}")
set(sources_string "set(sources\n${sources_string})")
file(WRITE sources.cmake "${sources_string}")
The reason why this works is because execute_process() occurs at configure time.
You could, of course, generate sources.cmake via some other tool or IDE then you wouldn't need scansources.cmake or execute_process().

Cmake add command line argument to binary

I create a binary myBinary via cmake/CMakeLists.txt.
I would like to "include" default options on my binary.
In other words, I want my binary to be called with myBinary --option myopt even when I just run ./myBinary
How can I do that?
CMake does not have built-in support for you you want to do.
One solution is to do as #Youka said - change the source code of your program.
Another solution that I have used sometimes is to autogenerate a script that executes an executable:
# Create startup script
MACRO(GEN_START_SCRIPT binName)
# Generate content
SET(fileContent
"#!/bin/bash\n"
"\n"
"# This startup script is auto generated - do not modify!\n"
"\n"
"${binName} -a 23 -b 34 -c 976\n"
"\n"
)
# Write to file
SET(fileName ${CMAKE_CURRENT_BINARY_DIR}/${binName}.sh)
FILE(WRITE ${fileName} ${fileContent})
ENDMACRO()
Then call the macro after defining your executable:
ADD_EXECUTABLE(myBinary file1.c file.2)
GEN_START_SCRIPT(myBinary)
You can of course add other stuff to the script, like environment variables etc.
If you're in control of the sources and you want different default behavior... change the sources!
This is in no way a build system issue (CMake or otherwise).

what triggers scons to build files when I have a custom builder?

I'm going nuts trying to control when files are built in scons. I have a very simple example build tree (see below), with a Poem builder that just takes a .txt file and converts it to lower case in a corresponding .eectxt file.
In my SConstruct and SConscript files, I declare dependencies of 3 .txt files.
But I can't figure out what's putting these into the default build!
sconstest/
SConstruct
tiger.txt
src/
SConscript
hope.txt
jabberwocky.txt
where the *.txt files are poems and my SConstruct and SConscript look like this:
SConstruct:
env = Environment();
def eecummings(target, source, env):
if (len(target) == 1 and len(source) == 1):
with open(str(source[0]), 'r') as fin:
with open(str(target[0]), 'w') as fout:
for line in fin:
fout.write(line.lower());
return None
env['BUILDERS']['Poem'] = Builder(action=eecummings, suffix='.eectxt', src_suffix='.txt');
Export('env');
poems = SConscript('src/SConscript');
tigerPoem = env.Poem('tiger.txt');
src/SConscript:
Import('env');
input = ['jabberwocky.txt', 'hope.txt'];
output = [env.Poem(x) for x in input];
Return('output');
What I want to do is to declare the dependency of the .eectxt files from the corresponding .txt files, but not cause them to be built unless I explicitly put them into the Default() build in the SConstruct file, or I request them explicitly at the command line.
How can I do this?
By default, a directory depends on all files and/or targets which reside in it.
So running:
scons
Will then build all targets under the current directory.
I figured out how to do what I want, but I still don't understand why I need to do it this way. Acceptance to the first decent answer that explains it.
Here's what works, if I add the following to the root SConstruct file:
env.Ignore('.', tigerPoem);
env.Ignore('src', poems);
env.Alias('poems', [tigerPoem]+poems);
This ignores the 3 poems from the default target, and then adds them as targets aliased to "poems", so if I run scons it builds nothing, but if I run scons poems it builds the files.
Why does this work? Why does calling env.Poem(...) add something to the default targets?

In scons, how can I inject a target to be built?

I want to inject a "Cleanup" target which depends on a number of other targets finishing before it goes off and gzip's some log files. It's important that I not gzip early as this can cause some of the tools to fail.
How can I inject a cleanup target for Scons to execute?
e.g. I have targets foo and bar. I want to inject a new custom target called 'cleanup' that depends on foo and bar and runs after they're both done, without the user having to specify
% scons foo cleanup
I want them to type:
% scons foo
but have scons execute as though the user had typed
% scons foo cleanup
I've tried creating the cleanup target and appending to sys.argv, but it seems that scons has already processed sys.argv by the time it gets to my code so it doesn't process the 'cleanup' target that I manually append to sys.argv.
you shouldn't use _Add_Targets or undocumented features, you can just add your cleanup target to BUILD_TARGETS:
from SCons.Script import BUILD_TARGETS
BUILD_TARGETS.append('cleanup')
if you use this documented list of targets instead of undocumented functions, scons won't be confused when doing its bookkeeping. This comment block can be found in SCons/Script/__init__.py:
# BUILD_TARGETS can be modified in the SConscript files. If so, we
# want to treat the modified BUILD_TARGETS list as if they specified
# targets on the command line. To do that, though, we need to know if
# BUILD_TARGETS was modified through "official" APIs or by hand. We do
# this by updating two lists in parallel, the documented BUILD_TARGETS
# list, above, and this internal _build_plus_default targets list which
# should only have "official" API changes. Then Script/Main.py can
# compare these two afterwards to figure out if the user added their
# own targets to BUILD_TARGETS.
so I guess it is intended to change BUILD_TARGETS instead of calling internal helper functions
One way is to have the gzip tool depend on the output of the log files. For example, if we have this C file, 'hello.c':
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
And this SConstruct file:
#!/usr/bin/python
env = Environment()
hello = env.Program('hello', 'hello.c')
env.Default(hello)
env.Append(BUILDERS={'CreateLog':
Builder(action='$SOURCE.abspath > $TARGET', suffix='.log')})
log = env.CreateLog('hello', hello)
zipped_log = env.Zip('logs.zip', log)
env.Alias('cleanup', zipped_log)
Then running "scons cleanup" will run the needed steps in the correct order:
gcc -o hello.o -c hello.c
gcc -o hello hello.o
./hello > hello.log
zip(["logs.zip"], ["hello.log"])
This is not quite what you specified, but the only difference between this example and your requirement is that "cleanup" is the step that actually creates the zip file, so that is the step that you have to run. Its dependencies (running the program that generates the log, creating that program) are automatically calculated. You can now add the alias "foo" as follows to get the desired output:
env.Alias('foo', zipped_log)
In version 1.1.0.d20081104 of SCons, you can use the private internal SCons method:
SCons.Script._Add_Targets( [ 'MY_INJECTED_TARGET' ] )
If the user types:
% scons foo bar
The above code snippet will cause SCons to behave as though the user had typed:
% scons foo bar MY_INJECTED_TARGET

Resources