If condition inside the %Files section on a SPEC file - rpmbuild

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.

Related

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

GNU-Make: how to copy files from source to destination

I know this is a basic question but I'm missing something fundamental about makefiles.
Take this simple rule/action:
doc: ${SRC_DIR}/doc/dir1/file1.pdf ${SRC_DIR}/doc/dir1/file2.pdf
cp $? ${DEST_DIR}/doc/
the first time I run it, it copies file1.pdf and file2.pdf to the destination/doc directory. Perfect. I'm expecting the next time I run it, for it to do nothing. The source files haven't changed, aren't they a dependency? But when I run I get :
cp : cannot create regular file ..... :Permission denied.
so, 2 questions:
1) Why is it trying to do it again? When I run make -d I see it eventually says: No need to remake target .../file1.pdf and .../file2.pdf but then
it says : must remake target 'doc'
If it doesn't need to make either pdf file, why does it need to make doc?
2) say the pdf files had changed in the source, they are read only though, so it gets the permission denied error. How do you get around this?
A make rule:
target: preqreq0 prereq1...
command
...
says that target needs to be (re)made if it does not exist or is older than
any of the prerequisites preqreq0 prereq1..., and that target shall be
(re)made by running the recipe command ....
Your rule:
doc: ${SRC_DIR}/doc/dir1/file1.pdf ${SRC_DIR}/doc/dir1/file2.pdf
cp $? ${DEST_DIR}/doc/
never creates a file or directory doc, so doc will never exist when
the rule is evaluated (unless you create doc by other means), so the recipe
will always be run.
The kind of target that I believe you want doc to be is a phony target,
but you are going about it wrongly. A reasonable makefile for the purpose would
be:
SRC_DIR := .
DEST_DIR := .
PDFS := file1.pdf file2.pdf
PDF_TARGS := $(patsubst %,$(DEST_DIR)/doc/%,$(PDFS))
.PHONY: doc clean
doc: $(PDF_TARGS)
$(DEST_DIR)/doc/%.pdf: $(SRC_DIR)/doc/dir1/%.pdf
cp $< $#
clean:
rm -f $(PDF_TARGS)
I recommend The GNU Make documentation
As for your second problem, how to overwrite "readonly" files, it is unrelated to make.
You cannot overwrite files to which you do not have write permission, regardless
of the means by which you try to do it. You must get write permission to any files
that you need to write to. It is a system administration matter. If you do not
understand file permissions you may find help at sister-site Unix & Linux
or serverfault

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

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.

Can a pre-commit Git hook zip a directory and add it to the repository?

I'm doing development on a Wordpress plugin. My development directory contains a lot of development-specific stuff (e.g. Grunt files, Sass files, the git repository itself, etc.).
Obviously, I don't want to distribute this folder containing all of those development files; people don't want a few MB of Grunt files when they download my Wordpress plugin.
Up until now, though, my "release" process has been cumbersome:
Commit the Git changes
Zip the entire folder
Open the zip file and delete the .git folder, grunt files, and all the other development-specific files
Release the new zip
I don't know the best way to accomplish this, but I'm very vaguely familiar with Git hooks, and I had this thought: could I set up a Git hook that would zip ONLY the needed production files into a ZIP file and store it with the repo? That way, every time I commit it would automatically create a new release ZIP.
Is that possible? If so, could someone point me in the right direction?
Oh also, I'm on Windows (・_・;). So I'm hoping that there's a way to do it on Windows.
I can't speak for Windows, but:
It's technically possible to do that sort of thing in a pre-commit hook.
Don't.
A pre-commit hook that modifies "what you will commit" is annoying (if nothing else, it violates the "rule of least astonishment", where your version control system simply stores the versions you tell it to store). Apart from that, storing large pre-compressed binaries interferes with git's attempt to save space in pack files, and will cause rapid repository bloat, poor performance, running out of memory, and so on. A ZIP-archive is a pre-compressed binary and hence will behave badly.
In general, a more reasonable "hook-y" way to handle releases is to set up a "release server" to which you push new releases, and have the push trigger the archive-generation. (There are ways to do this without a separate server / repository, and you can do it in a more pull-style fashion, but the push-style is easy to illustrate.)
[Edit: I had originally considered git archive but did not realize you could get it to exclude files conveniently, so wrote up the below instead. So, jthill's answer is better and should be one's first resort. I'll leave this in place as an alternative for some case where for some reason, git archive might not do.]
For instance, here's a server-side post-receive hook code fragment that checks whether a branch whose name matches release* has been pushed-to, and if so, invokes a shell function with the name of the branch (once for each such branch):
#! /bin/sh
NULL_SHA1=0000000000000000000000000000000000000000
scan()
{
local oldsha newsha fullref shortref
local optype
while read oldsha newsha fullref; do
case $oldsha,$newsha in
$NULL_SHA1,*) optype=create;;
*,$NULL_SHA1) optype=delete;;
*) optype=update;;
esac
case $fullref in
refs/heads/*)
reftype=branch
shortref=${fullref#refs/heads/}
;;
*)
reftype=other
shortref=fullref
;;
esac
case $optype,$reftype,$shortref in
create,branch,release*|update,branch,release*)
do_release $shortref;;
esac
done
}
scan
(much of the above is boilerplate, which I have stripped down to essentials). You would have to write the do_release function, which might resemble (totally untested):
do_release()
{
local tmpdir=/tmp/build.$$ # or use mktemp -d
# $tmpdir/index is git's index; $tmpdir/t is the work tree
trap "rm -rf $tmpdir; exit 1" 1 2 3 15
rm -rf $tmpdir
mkdir $tmpdir/t
GIT_INDEX_FILE=$tmpdir/index GIT_WORK_TREE=$tmpdir/t git checkout $1
# now clean out grunt files and make zip archive
(cd $workdir/t; rm -rf grunt; zip ../t.zip .)
# put completed zip archive in export location, name it
# based on the branch name
mv $workdir/t.zip /place/where/zip/files/live/$1.zip
# clean up temp dir now, and no longer need to clean up
# on signal related abort
rm -rf $tmpdir
trap - 1 2 3 15
}
There's actually a command for this, git archive.
git archive master -o wizzo-v1.13.0.zip
See the EXAMPLES section, you can select paths, add prefixes to them, define custom postprocessing by output extension, and some more minor tweaks.
Also see the ATTRIBUTES section: you can give files -- arbitrary patterns, really -- an export-ignore attribute to exclude them from archives.
It's got a bunch more handy-dandies, you can get archives from remote repos, expand arbitrary git log --pretty=format: placeholders, the git manpages are definitely worth whatever time you can invest in them.

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