Unpacking sources with additional options during the PREP or SETUP stage? - rpmbuild

I'm working on packaging a library in an RPM. Fedora's RPM Build manual, 8.2.3.2, The PREP Section states:
The prep section, short for prepare, defines the commands necessary to prepare for the build. If you are starting with a compressed tar archive (a tarball) of the sources, the prep section needs to extract the sources.
For example:
%prep
%setup -q
The prep section starts with a %prep statement.
This example uses the %setup RPM macro, which knows about tar archives, to extract the files. In most cases, this will be all you need in your spec file prep section.
We have a ZIP, and it needs to be extracted with the -a option. Otherwise, the patch step fails due to line endings.
How do I pass the -a option to the zip program through the PREP or SETUP section? (I'm not sure which section or stage needs it, but I know its one of them).

%prep is the section of the specfile.
%setup is a macro within the %prep section. You don't need to use it. Take it out and manually write out all the commands you need to unzip the source into a directory named packagename-versionnumber.
An example of what %setup expands to, along with more options it accepts, can be found at rpm.org.

Related

Disable parallel execution in make

I have a building process that creates a header file. In the second stage, several source files are generated from that header file. Then these source files are built into a binary. If anyone is interested these sources are generated with gSOAP utilities (wsdl2h, soapcpp2).
I have made Makefile.am, etc to build these sources, but there are problems when I want to use parallel execution.
Makefile.am would look something like this in a very simplified form
## generate header file
service.h : service.wsdl
wsdl2h -o $# service.wsdl
## list of generated source files
generated_files = source1.cpp source2.cpp source3.cpp
## generate source files
$(generated_files) : service.h
soapcpp2 $^
## build binary
binary: $(generated_files)
gcc -o $# $^
The rules say that service.h will be generated if service.wsdl changes. If service.h changes, soapcpp2 will generate source?.cpp files with one command execution.
Everything works fine until I try to build in parallel (for instance make -j4). The problematic line is the last one which generates many soruce files. If running in parrallel all these files are generated many times, while other make processes already try to compile them.
I followed instructions to disable parallel https://www.gnu.org/software/make/manual/html_node/Parallel-Disable.html, but with no success. If I try
.NOTPARALLEL: $(generated_files)
or
.NOTPARALLEL: service.h
The parallel execution just does not work any more. I also tried with .WAIT, and got no rule to make target .WAIT.
First, the .WAIT special target was introduced in GNU make 4.4. Since you are getting a no rule to make target error for it, it's clear you're using an older version which doesn't support it. It's usually a good idea to include the version of whatever tool you're using when asking for help.
The best thing to do is not disable parallelism but instead tell make that a single invocation of the recipe will generate all the files. If you have GNU make 4.3, then you can use a "grouped target" rule, like this:
## generate source files
$(generated_files) &: service.h
soapcpp2 $^
the &: here tells make that instead of building each target with a different invocation of the recipe, which is the default, a single invocation of the recipe builds all the targets.
If you don't have GNU make 4.3 then you'll need to play a trick to get the same behavior, something like this:
## generate source files
.sentinel : service.h
soapcpp2 $^
#touch $#
$(generated_files) : .sentinel ;
## build binary
binary: $(generated_files)
gcc -o $# $^
This has all the generated files depend on a single file .sentinel (you can name it whatever you want), which is the one make knows is generated by the recipe that also creates all the other source files. This isn't perfect but it will work for simple situations.

Why use %files directive in RPM spec file and how to automate file listing in that section?

Why does %files needed and why it cannot be created automatically by listing $RPM_BUILD_ROOT directory contents?
For example, in that spec file i have to use some modifications in it in script, that build an RPM package for me. I have to cd into ~/RPM/SOURCES, then find . in it and echo each line into .spec file to setup %files section. It can be done automatically by the tool using the same techniuque i do in the background but from $RPM_BUILD_ROOT directory instead?
%install
mkdir -p $RPM_BUILD_ROOT/opt/MyCompany/MyProduct/
cp -rf -- ~/RPM/SOURCES/* $RPM_BUILD_ROOT/opt/MyCompany/MyProduct/
%files
/opt/MyCompany/MyProduct/file_1
/opt/MyCompany/MyProduct/file_2
/opt/MyCompany/MyProduct/file_3
... - a lot of lines here
/opt/MyCompany/MyProduct/file_100000
UPD:
Yes it is possible to use all copied files automatically (Doc http://ftp.rpm.org/max-rpm/s1-rpm-inside-files-list-directives.html)
In my case %files can be rewritten as that
%files
/opt/MyCompany/MyProduct/*
It will take files from $RPM_BUILD_ROOT/opt/MyCompany/MyProduct, so to use star we need to omit build root which is $RPM_BUILD_ROOT or %{buildroot} (Which is default path where rpm searches for files IIUC)
Why does %files needed and why it cannot be created automatically by listing
$RPM_BUILD_ROOT directory contents?
It is common that one spec file build creates multiple (sub)packages - then also
multiple %files sections are needed.
Even though there exist some automatic %files
section
generators,
nb there's the
%files -f option,
in general it isn't possible to split the files automatically.
Worth saying that most of the package maintainers don't maintain the software
source code, but just package the "upstream" releases. If the %files sections
are rather manually maintained (in contrast with the wildcard patterns), the
package maintainer has everything a bit more under control because he is
notified about the changes in the software installation layout (e.g. when a new
file appears in /usr/bin with a new release, rpmbuild starts complaining that
there's a new "unpackaged" file, see below).
It can be done automatically by the tool using the same techniuque i do in the
background but from $RPM_BUILD_ROOT directory instead?
You don't even have to run find manually. As long as %install creates the
files in $RPM_BUILD_ROOT, if you don't mention them in %files - you get reports
like those:
$ rpmbuild -bb *.spec
...
Checking for unpackaged file(s): /usr/lib/rpm/check-files /home/user/rpmbuild/BUILDROOT/test-1-1.x86_64
error: Installed (but unpackaged) file(s) found:
/usr/bin/not-packaged
RPM build errors:
Installed (but unpackaged) file(s) found:
/usr/bin/not-packaged

How to build a rpm that installs host dependent files

I have to build one rpm that copies the contents of file A to /path/to/tartetfile if the hostname is A. In all other cases the contents of B should be copied to /path/to/targetfile. I'm aware that this may be a misusage of rpm, but I still have to do it like this. Do you have any ideas how to get this done in an elegant way?
My solution at the moment would be to create an empty /path/to/targetfile in my BUILD directory as well as a /tmp/contents.tar.gz that contains the files A and B. In the postinstall routine i then would extract the relevant parts of /tmp/contents.tar.gz to /path/to/targetfile and delete the tarball afterwards. In the pre-uninstall routine I'd then touch the /tmp/contents.tar.gz to supress rpm reporting errors for an already deleted file.
To me this seems to be a very dirty way to get this done. Do you have better ones?
If you plan on abusing rpm for things it was not desinged for, you'll have to do dirty tricks.
I don't see another workaround for you. I fail to see the use of removing the tar.gz etc, unless that (little?) extra space is really a problem for you. I would propose:
package all files (A and B) into some specific directory (/usr/lib/your-package or whatever), not in compressed format.
in the %post section create just symlinks so that /path/to/targetfile points to /usr/lib/your-package/A or /usr/lib/your-package/B (symlinks take up almost no space). This has the additional value that ls -l /path/to/targetfile will show you which which file it points to, giving you the information whether this is file A or B.
in your %files section declare %ghost /path/to/targetfile for a nice cleanup upon removal.

If condition inside the %Files section on a SPEC file

I'm kinda a new to writing spec files and building RPM's. Currently I have one RPM that is supposed to deploy some files in 1 of 2 possible directories that will vary with the OS.
How can I, within the %files section, verify them? I can't use variable...I can't verify both paths because one will for sure fail...I tried to define a macro earlier in the %install section but it will be defined just once and won't be redefined on every RPM installation...
what can I do here?
Thanks
I had a similar situation where additional files were included in the RPM in case of a DEBUG build over and above all files in the RELEASE build.
The trick is to pass a list of files to %files alongwith a regular list of files below it:
%install
# Create a temporary file containing the list of files
EXTRA_FILES=$RPM_BUILD_ROOT/ExtraFiles.list
touch %{EXTRA_FILES}
# If building in DEBUG mode, then include additional test binaries in the package
%if %{build_mode} == "DEBUG"
# %{build_mode} is a variable that is passed to the spec file when invoked by the build script
# Like: rpmbuild --define "build_mode DEBUG"
echo path/to/file1 > %{EXTRA_FILES}
echo path/to/file2 >> %{EXTRA_FILES}
%endif
%files -f %{EXTRA_FILES}
path/to/release/file1
path/to/release/file2
In your case, you can leverage the %if conditional in the %install section, use the OS as a spec variable passed to rpmbuild (or detect it in the RPM spec itself) and then pass the file containing the list to %files
The %files section can have variables in it, but usually this would be something like your path that is defined so you don't have to repeat it a bunch. so %{long_path}/file_name, where long_path was defined earlier in the spec file. the %files section is all the information that goes into the RPM database, and is created when you build the RPM so you won't be able to change those values based on machine information when installed.
If you really want to do this, you could include a tar file inside of the main tarball that gets extracted depending on certain conditions (since the spec file is just bash). Now keep in mind this is an awful idea. The files won't be tracked by the RPM database, so when you remove the RPM these files will still exist.
In reality you should build two RPMs, this will allow for better support going forward into the future in the event you have to hand this off to someone, as well as preserving your own sanity a year from now when you need to update the RPM.
This is how I solved my problem
step 1 :
In Build section .. somewhere I wrote :
%build
.....
#check my condition here & if true define some macro
%define is_valid %( if [ -f /usr/bin/myfile ]; then echo "1" ; else echo "0"; fi )
#after his normal continuation
.....
...
Step 2: in install section
%install
......
#do something in that condition
if %is_valid
install -m 0644 <file>
%endif
#rest all your stuff
................
Step 3:in files section
%files
%if %is_valid
%{_dir}/<file>
%endif
That's it
It works.
PS : I cannot give you full code hence giving all useful snippet
Forrest suggests the best solution, but if that is not possible practical you can detect the OS version at runtime in the post-install section, move the script to the appropriate location, and then delete it post-uninstall, eg:
# rpm spec snippets
%define OS_version %(hacky os detection)
...
Source2: script.sh
...
%install
install %{_sourcedir}/script.sh %{buildroot}/some/known/location
...
%post
%if %{OS_version} == "..."
mv /some/known/location/script.sh /distro/specific/script.sh
%elif %{OS_version} == "..."
...
%preun
rm -rf /all/script/locations
Much more error prone than building different RPMs on different OSes, but will scale a little better if you need to support many different OSes.

Can 'make' check if mtime of a dependency is *different* between runs, not just if it's newer than target?

If foo_user.cpp depends on foo.h, then foo_user.cpp is built, and then foo.h's modification time is set to further in the past, make will not rebuild foo_user.cpp (because foo.cpp is 'newer'). I'd prefer it if make recorded the modification times of dependencies, and if they changed at all (newer or older), to consider targets of that dependency to be out of date. Can GNU make do this? If not, is there an easy alternative?
In case you're curious how this situation arises: foo.h resides in a symlinked folder. The symlink may point to the foolib-1.0 folder, the foolib-2.0 folder, etc. When the symlink points at a different version of the library, even an older version, foo_user.cpp should be rebuilt. If I simply specifiy symlinkfolder/foo.h as a dependency of foo_user.cpp, make only pays attention to the timestamp of foo.h, not the timestamp of the symlink'd directory through which foo.h is accessed. I can't add the symlink itself as a dependency, because the make rule is generated by the compiler (GCC has a special flag that when given causes it to output a make rule for all the headers a source file depends on).
I'm trying to understand why you can't just add the symlink as a dependency. I imagine your automatic dependencies are on one line, but you can have as many as you want.
x.o: a.h b.h
x.o: c.h
x.o: d.h
But having said that, it seems likely that make will stat the symlink's target, and not the symlink itself, so that may not DTRT. I suppose you could just touch a file somewhere whenever you make the symlink, but I also suppose you've already thought of that...
You could have a rule that runs ls -id link/. > test, which will put the inode number of the link target directory in test. You could then cmp test save, where save is from the last run. You could then have that make rule do make clean && make target if they are different.
targetwrapper:
ls -id link/. > test
cmp test save || make clean
make realtarget
cp test save
clean:
echo cleaned
realtarget:
echo made
No, Make does not support this. You may wish to consider using another build system such as SCons, which does not rely solely on the timestamp but actually computes the MD5 hash of source files and bases its decisions on the hashes.
From "What makes SCons better?" on its web site:
Reliable detection of build changes using MD5 signatures; optional, configurable support for traditional timestamps.
While make doesn't support it out of the box, you can program it.
include more_deps
ifneq ($(MAKE_RESTARTS),)
more_deps:
if (foolink.old differs from what foolink points to) ; then \
readlink foolink > foolink.old ; \
echo "foo_user: foolink_trigger" > more_deps ; \
touch foolink_trigger ; \
else \
echo "" > more_deps ;\
fi
endif
foo_user: foo_user.cpp
g++ $^ -o $#
Here you include makefile more_deps which sometimes will include the dependency on the symlink's trigger. Trigger is a special intermediate flie, all the meaningful informaion in which is its timestamp. When the symlink changes, the timestamp of the trigger is updated to current time (see touch), thus making foo_user outdated and it is the rebuilt.
include and MAKE_RESTARTS are needed to restart make after calculating the dependency described above. If the makefile being included is a target itself, the target is considered to be rebuilt, is rebuilt and then make restarts and re-reads makefile. But when it reads makefile for the second time, it doesn't see more_deps as a target, because MAKE_RESTARTS variable expands to non-empty string.
In fact, the line with if can sound like this:
more_deps:
if (any condition you want with $(VARIABLES) possible!) ; then \
update a file that holds the previous state ;\
...
Through which process do you change the symlink? You could add a make clean type of action to the script that changes the symlink.
You could also set up a "header working folder" in with you let make copy your header files, where the copied header files are dependent on their original and the symlink. The dependencies generated by GCC only take the working headers into account and won't clash with your copy headers into the working folder part of your Makefile.

Resources