Creating Symlinks in /usr/bin During RPM Package Installation: Best Practices for Non-Root Builds


2 views

When building RPM packages as a non-root user, creating symlinks in system directories like /usr/bin presents a common pain point. The error message "Permission denied" occurs because rpmbuild doesn't have write permissions to these protected paths during the build process.

The key is to create symlinks within your build root ($RPM_BUILD_ROOT) rather than attempting to modify the live filesystem. Here's the correct way to implement this in your spec file:

%install
rm -rf $RPM_BUILD_ROOT
make DESTDIR=$RPM_BUILD_ROOT install

# Create directory structure in build root
mkdir -p ${RPM_BUILD_ROOT}%{_bindir}

# Create symlinks that will be installed to /usr/bin
ln -sf /opt/bupc2.8/bin/upcc ${RPM_BUILD_ROOT}%{_bindir}/upcc
ln -sf /opt/bupc2.8/bin/upcrun ${RPM_BUILD_ROOT}%{_bindir}/upcrun
# Additional symlinks here...

When implementing this solution, keep these technical details in mind:

  • Always use RPM macros like %{_bindir} rather than hardcoding paths
  • Ensure your %files section includes all created symlinks
  • The symlinks will be properly created during package installation by root
  • Consider using alternatives system for competing commands

Here's how the complete solution integrates with your existing spec file:

%files
%defattr(-,root,root)
/opt/bupc2.8
%config /opt/bupc2.8/etc

# Include all symlinks that should appear in /usr/bin
%{_bindir}/upcc
%{_bindir}/upcc_multi
%{_bindir}/upcc_multi.pl
%{_bindir}/upcdecl
%{_bindir}/upcrun
%{_bindir}/upc_trace

# Include man page symlinks
%{_mandir}/man1/upcc.1
%{_mandir}/man1/upcdecl.1
%{_mandir}/man1/upcrun.1
%{_mandir}/man1/upc_trace.1

After building your RPM, verify the contents using:

rpm -qlp yourpackage.rpm

This should show all your symlinks listed under /usr/bin, confirming they'll be properly installed.


When building RPM packages as a non-root user, one common hurdle is creating symlinks in system directories like /usr/bin. The permission denied errors occur because rpmbuild runs with your user privileges, which typically don't have write access to these protected locations.

The solution lies in properly utilizing $RPM_BUILD_ROOT during package creation. Here's how your spec file should handle symlinks:

%install
rm -rf $RPM_BUILD_ROOT
make DESTDIR=$RPM_BUILD_ROOT install

# Create directory structure under build root
mkdir -p ${RPM_BUILD_ROOT}%{_bindir}
mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man1

# Create relative symlinks within build root
ln -sf ../opt/bupc2.8/bin/upcc ${RPM_BUILD_ROOT}%{_bindir}/upcc
ln -sf ../opt/bupc2.8/bin/upcrun ${RPM_BUILD_ROOT}%{_bindir}/upcrun

When implementing symlinks in your RPM:

  • Use relative paths (../opt/...) rather than absolute paths for better portability
  • Ensure the target paths exist in your %files section
  • Include both the symlinks and their targets in your package manifest

Here's an improved version of your spec file with proper symlink handling:

%files
%defattr(-,root,root)
/opt/bupc2.8
%{_bindir}/upcc
%{_bindir}/upcrun

# For man pages
%{_mandir}/man1/upcc.1.gz
%{_mandir}/man1/upcrun.1.gz

If you encounter issues:

  1. Inspect the build root contents: find $RPM_BUILD_ROOT
  2. Verify symlink targets exist in the build environment
  3. Check file permissions in the final RPM with rpm -qlv yourpackage

For more complex scenarios, you can create symlinks during installation:

%post
ln -sf /opt/bupc2.8/bin/upcc /usr/bin/upcc 2>/dev/null || :

%postun
rm -f /usr/bin/upcc 2>/dev/null || :

Remember this approach requires the package to be installed with root privileges.