title: Building RPM package from cross-compiled binaries Go app tarballs
body: <p>I'm trying to create my first RPM packages cross-compiled with Go.</p>
  
  <p>Here is the Makefile which contains all the required information:</p>
  
  <pre><code>APPNAME?=helloworld
  VERSION?=v1.0.0
  
  APPANDVER := ${APPNAME}-$(VERSION)
  
  # Build flags
  LDFLAGS := -ldflags "-s -w -X=main.VERSION=$(VERSION)"
  
  # Temporary directory for common files when creating tarballs
  RELEASETMPDIR := $(shell mktemp -d -t ${APPNAME}-${VERSION}-release-XXXXXX)
  
  # Cross-compile to these CPUs
  # https://golang.org/doc/install/source#environment
  LINUX_ARCHS := amd64 arm arm64 ppc64 ppc64le
  
  default: release-bin
  
  main.go:
      @echo 'package main' &gt; $@
      @echo 'import "fmt"' &gt;&gt; $@ 
      @echo '// Replaced with version when building' &gt;&gt; $@ 
      @echo 'var VERSION = "v0.0.0"' &gt;&gt; $@
      @echo 'func main() {' &gt;&gt; $@
      @echo '  fmt.Println("Hello, world!")' &gt;&gt; $@
      @echo '}' &gt;&gt; $@
      @go fmt $@
  
  README.md:
      @echo 'This is a README for $(APPNAME) $(VERSION)' &gt; $@
      @echo '$(APPNAME) simply prints "Hello, world!"' &gt;&gt; $@
  
  LICENSE:
      @echo 'You can do what ever you want with $(APPNAME).' &gt; $@
      @echo 'You have all the responsibility!' &gt;&gt; $@
  
  # Build for all listed architectures
  linux-build: main.go 
      @for arch in $(LINUX_ARCHS); do \
        echo "GNU/Linux build... $$arch"; \
        CGO_ENABLED=0 GOOS=linux GOARCH=$$arch go build $(LDFLAGS) -v -o ./bin/linux-$$arch/${APPNAME} . ; \
      done
  
  # Copy common files used in binary tarball releases
  copycommon: README.md LICENSE
      @echo "Copying common files to temporary release directory '$(RELEASETMPDIR)'.."
      @mkdir "$(RELEASETMPDIR)/bin"
      @cp -v "LICENSE" "$(RELEASETMPDIR)"
      @cp -v "README.md" "$(RELEASETMPDIR)"
      @mkdir --parents "$(PWD)/release/${VERSION}"
  
  # Create binary release tarballs for each CPU architecture
  compress-linux: linux-build
      @for arch in $(LINUX_ARCHS); do \
        echo "GNU/Linux tar... $$arch"; \
        cp -v "$(PWD)/bin/linux-$$arch/${APPNAME}" "$(RELEASETMPDIR)/bin"; \
        cd "$(RELEASETMPDIR)"; \
        tar --numeric-owner --owner=0 --group=0 -zcvf "$(PWD)/release/${VERSION}/$(APPANDVER)-linux-$$arch.tar.gz" . ; \
        rm "$(RELEASETMPDIR)/bin/${APPNAME}"; \
      done
  
  # Move all to temporary directory and compress with common files
  compress-everything: copycommon compress-linux
      @echo "$@ ..."
      rm -rf "$(RELEASETMPDIR)/*"
  
  # Create tarballs which has common files and different bin/${APPNAME} per CPU architecture
  release-bin: linux-build compress-everything
      @echo "release done..."
  
  # Linux distributions
  release-ldistros: ldistro-rpm
      @echo "Linux distros release done..."
  
  release/linux/rpm:
      @mkdir --parents ./release/linux/rpm
  
  # RPM spec file (probably wrong)
  release/linux/rpm/package.spec: release/linux/rpm
      @echo 'Name: ${APPNAME}' &gt; $@
      @echo 'Version: %{_version}' &gt;&gt; $@
      @echo 'Release: 1%{?dist}' &gt;&gt; $@
      @echo 'Summary: Hello world' &gt;&gt; $@
      @echo 'URL: https://example.org/${APPANDVER}/' &gt;&gt; $@
      @echo 'Group: Applications/Utilities' &gt;&gt; $@
      @echo 'License: Apache-2.0' &gt;&gt; $@
      @echo '%description' &gt;&gt; $@
      @echo '${APPNAME} is a command line program which prints "Hello, world!"' &gt;&gt; $@
      @echo '%setup -q' &gt;&gt; $@
      @echo '%clean' &gt;&gt; $@
      @echo '%files' &gt;&gt; $@
      @echo '%license /usr/share/licenses/%{NAME}/LICENSE' &gt;&gt; $@
      @echo '%doc /usr/share/doc/%{NAME}/README.md' &gt;&gt; $@
      @echo '/usr/bin/${APPNAME}' &gt;&gt; $@
      @echo '%install' &gt;&gt; $@
      @echo 'install -Dm755 "usr/bin/%{NAME}" -t "/usr/bin"' &gt;&gt; $@
  
  # Create RPM package for each CPU architecture from tarballs (probably wrong)
  ldistro-rpm: "release/linux/rpm/package.spec
      @for arch in $(LINUX_ARCHS); do \
        echo "Generating RPM... $$arch"; \
        tempdir=$$(mktemp -d -t $(APPANDVER)-rpm-XXXXXX) ; \
        echo "  &gt;&gt; Using temporary directory $$tempdir" ; \
        cd "$$tempdir" ; \
        mkdir --parents {SOURCES,RPMS,SPECS,SRPMS,BUILD,tmp} ; \
        cp "$(PWD)/release/linux/rpm/package.spec" "SPECS/${APPNAME}" ; \
        cd "BUILD"; \
        echo "  &gt;&gt; Extracting source binary package.." ; \
        tar -xzf "$(PWD)/release/${VERSION}/$(APPANDVER)-linux-$$arch.tar.gz" . ; \
        echo "  &gt;&gt; Generating directory structure in temp dir.." ; \
        mkdir --parents ./usr/bin/ ; \
        mv ./bin/${APPNAME} ./usr/bin/ ; \
        rm -rf ./bin ; \
        mkdir --parents ./usr/share/licenses/${APPNAME}/ ; \
        mv LICENSE ./usr/share/licenses/${APPNAME} ; \
        mkdir --parents ./usr/share/doc/${APPNAME}/ ; \
        mv README.md ./usr/share/doc/${APPNAME} ; \
        cd .. ; \
        echo "  &gt;&gt; Building RPM package.." ; \
        sudo rpmbuild -vv --nosignature --nodebuginfo --dbpath "$$tempdir" --root "$$tempdir" --buildroot "./BUILD" --target $$arch --define "_tmppath /tmp" --define "_topdir ." --define "_version ${VERSION}" --define "_buildhost localhost" --define "_rpmfilename $(APPANDVER)-$$arch.rpm" -bb "SPECS/${APPNAME}" &amp;&amp; \
        rpm -qlp --info "./RPMS/$(APPANDVER)-$$arch.rpm" &amp;&amp; \
        cp "./RPMS/$(APPANDVER)-$$arch.rpm" "$(PWD)/release/${VERSION}/" ; \
        echo "------------------------------------------------------------"; \
      done
  </code></pre>
  
  <p>Create <code>bin/$os-$cpuarch/$appname</code> binaries:</p>
  
  <pre><code>% make linux-build
  </code></pre>
  
  <p>Create the binary source tarballs to <code>release/$version/$appname-$version-$os-$cpuarch.tar.gz</code>:</p>
  
  <pre><code>% make compress-everything
  </code></pre>
  
  <p>Created file tree structure inside tarballs:</p>
  
  <pre><code>bin/$appname (different for each architecture)
  LICENSE
  README.md
  </code></pre>
  
  <p>This structure is locked in as there are also different OS tarballs too. Different OS build targets are removed in this minimized example for readability reasons.</p>
  
  <p>Generate RPMs for each CPU architecture:</p>
  
  <pre><code>% make ldistro-rpm
  </code></pre>
  
  <p>The current problem is that during RPM build the actual executables are being installed to the running system. AFAIK this should not happen. What I might be missing from the spec file or <code>ldistro-rpm</code> target? Also some <code>rpmbuild</code> examples seemed to only use <code>-bb</code> parameter but I couldn't find examples or figure out how to modify the <code>spec</code> file so that could work. The <code>ldistro-rpm</code> target seems overly complicated. Should some commands be in the spec file's <code>%install</code>, <code>%prep</code>, etc? Can you somehow use the <code>Source0</code> in the <code>spec</code> file and point it to the tarball instead of generating the directory structure in the <code>ldistro-rpm</code> target?</p>