On Sun, 2018-11-18 at 03:37 +0800, Jason Zaman wrote:
> Hey all,
> 
> I've been using Bazel (https://bazel.build/) to build TensorFlow for a
> while now. Here is a bazel.eclass I'd like to commit to make it easier
> for packages that use it to build. It's basically bits that I've
> refactored out of the TensorFlow ebuild that would be useful to other
> packages as well. I have a bump to sci-libs/tensorflow-1.12.0 prepared
> that uses this eclass and have tested a full install.
> 
> -- Jason
> 
> # Copyright 1999-2018 Jason Zaman
> # Distributed under the terms of the GNU General Public License v2
> 
> # @ECLASS: bazel.eclass
> # @MAINTAINER:
> # Jason Zaman <perfin...@gentoo.org>
> # @AUTHOR:
> # Jason Zaman <perfin...@gentoo.org>
> # @BLURB: Utility functions for packages using Bazel Build
> # @DESCRIPTION:
> # A utility eclass providing functions to run the Bazel Build system.
> #
> # This eclass does not export any phase functions.
> 
> case "${EAPI:-0}" in
>       0|1|2|3|4|5|6)
>               die "Unsupported EAPI=${EAPI:-0} (too old) for ${ECLASS}"
>               ;;
>       7)
>               ;;
>       *)
>               die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}"
>               ;;
> esac
> 
> if [[ ! ${_BAZEL_ECLASS} ]]; then
> 
> inherit multiprocessing toolchain-funcs
> 
> BDEPEND=">=dev-util/bazel-0.19"
> 
> # @FUNCTION: bazel_get_flags
> # @DESCRIPTION:
> # Obtain and print the bazel flags for target and host *FLAGS.
> #
> # To add more flags to this, append the flags to the
> # appropriate variable before calling this function
> bazel_get_flags() {
>       local i fs=()
>       for i in ${CFLAGS}; do
>               fs+=( "--conlyopt=${i}" )
>       done
>       for i in ${BUILD_CFLAGS}; do
>               fs+=( "--host_conlyopt=${i}" )
>       done
>       for i in ${CXXFLAGS}; do
>               fs+=( "--cxxopt=${i}" )
>       done
>       for i in ${BUILD_CXXFLAGS}; do
>               fs+=( "--host_cxxopt=${i}" )
>       done
>       for i in ${CPPFLAGS}; do
>               fs+=( "--conlyopt=${i}" "--cxxopt=${i}" )
>       done
>       for i in ${BUILD_CPPFLAGS}; do
>               fs+=( "--host_conlyopt=${i}" "--host_cxxopt=${i}" )
>       done
>       for i in ${LDFLAGS}; do
>               fs+=( "--linkopt=${i}" )
>       done
>       for i in ${BUILD_LDFLAGS}; do
>               fs+=( "--host_linkopt=${i}" )
>       done
>       echo "${fs[*]}"
> }
> 
> # @FUNCTION: bazel_setup_bazelrc
> # @DESCRIPTION:
> # Creates the bazelrc with common options that will be passed
> # to bazel. This will be called by ebazel automatically so
> # does not need to be called from the ebuild.
> bazel_setup_bazelrc() {
>       if [[ -f "${T}/bazelrc" ]]; then
>               return
>       fi
> 
>       # F: fopen_wr
>       # P: /proc/self/setgroups
>       # Even with standalone enabled, the Bazel sandbox binary is run for 
> feature test:
>       # 
> https://github.com/bazelbuild/bazel/blob/7b091c1397a82258e26ab5336df6c8dae1d97384/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java#L61
>       # 
> https://github.com/bazelbuild/bazel/blob/76555482873ffcf1d32fb40106f89231b37f850a/src/main/tools/linux-sandbox-pid1.cc#L113
>       addpredict /proc
> 
>       mkdir -p "${T}/bazel-cache" || die
>       mkdir -p "${T}/bazel-distdir" || die
> 
>       cat > "${T}/bazelrc" <<-EOF || die
>       startup --batch

Maybe indent this stuff to make it stand out from ebuild code.

> 
>       # dont strip HOME, portage sets a temp per-package dir
>       build --action_env HOME
> 
>       # make bazel respect MAKEOPTS
>       build --jobs=$(makeopts_jobs)
>       build --compilation_mode=opt --host_compilation_mode=opt
> 
>       # FLAGS
>       build $(bazel_get_flags)
> 
>       # Use standalone strategy to deactivate the bazel sandbox, since it
>       # conflicts with FEATURES=sandbox.
>       build --spawn_strategy=standalone --genrule_strategy=standalone
>       test --spawn_strategy=standalone --genrule_strategy=standalone
> 
>       build --strip=never
>       build --verbose_failures --noshow_loading_progress
>       test --verbose_test_summary --verbose_failures --noshow_loading_progress
> 
>       # make bazel only fetch distfiles from the cache
>       fetch --repository_cache="${T}/bazel-cache/" 
> --distdir="${T}/bazel-distdir/"
>       build --repository_cache="${T}/bazel-cache/" 
> --distdir="${T}/bazel-distdir/"
> 
>       build --define=PREFIX=${EPREFIX%/}/usr
>       build --define=LIBDIR=\$(PREFIX)/$(get_libdir)
> 
>       EOF
> 
>       tc-is-cross-compiler || \
>               echo "build --nodistinct_host_configuration" >> "${T}/bazelrc" 
> || die

Don't do || chains, they are unreadable.

> }
> 
> # @FUNCTION: ebazel
> # @USAGE: [<args>...]
> # @DESCRIPTION:
> # Run bazel with the bazelrc and output_base.
> #
> # If $MULTIBUILD_VARIANT is set, this will make an output_base
> # specific to that variant.
> # bazel_setup_bazelrc will be called and the created bazelrc
> # will be passed to bazel.
> #
> # Will automatically die if bazel does not exit cleanly.
> ebazel() {
>       bazel_setup_bazelrc
> 
>       # Use different build folders for each multibuild variant.
>       local base_suffix="${MULTIBUILD_VARIANT+-}${MULTIBUILD_VARIANT}"

Any reason not to use BUILD_DIR instead of reinventing it?

>       local output_base="${WORKDIR}/bazel-base${base_suffix}"
>       mkdir -p "${output_base}" || die
> 
>       einfo Running: bazel --output_base="${output_base}" "$@"
>       bazel --bazelrc="${T}/bazelrc" --output_base="${output_base}" $@ || die 
> "ebazel $@"

The common practice is to echo >&2 it.  Also, you output different
arguments than you execute which is going to confuse the hell out of
users who'll end up having to debug this.  You can use a trick like
the following to avoid typing args twice:

  set -- bazel --bazelrc...
  echo "${*}" >&2
  "${@}" || die ...

> }
> 
> # @FUNCTION: bazel_load_distfiles
> # @USAGE: <distfiles>...
> # @DESCRIPTION:
> # Populate the bazel distdir to fetch from since it cannot use
> # the network. Bazel looks in distdir but will only look for the
> # original filename, not the possibly renamed one that portage
> # downloaded. If the line has -> we to rename it back. This also
> # handles use-conditionals that SRC_URI does.

Why oh why do you have to implement custom parser for the ebuild syntax?
 That's just asking for horrible failures.

> #
> # Example:
> # @CODE
> # bazel_external_uris="http://a/file-2.0.tgz
> #     python? ( http://b/1.0.tgz -> foo-1.0.tgz )"
> # SRC_URI="http://c/${PV}.tgz
> #     ${bazel_external_uris}"
> #
> # src_unpack() {
> #     unpack ${PV}.tgz
> #     bazel_load_distfiles "${bazel_external_uris}"
> # }
> # @CODE
> bazel_load_distfiles() {
>       local src dst uri rename
> 
>       [[ "$@" ]] || die "Missing args"
>       mkdir -p "${T}/bazel-distdir" || die
> 
>       while read uri rename dst; do
>               src="${uri##*/}"
>               [[ -z $src ]] && continue

Please use ${foo} syntax in ebuilds, consistently.

>               if [[ "$rename" != "->" ]]; then
>                       dst="${src}"
>               fi
> 
>               [[ ${A} =~ ${dst} ]] || continue

Why are you doing regex match here?  Last I checked, we didn't use
regular expressions in SRC_URI.

> 
>               if [[ "$dst" == "$src" ]]; then
>                       einfo "Copying $dst to bazel distdir ..."
>               else
>                       einfo "Copying $dst to bazel distdir $src ..."

Are you using src and dst to mean the opposite?

>               fi
>               dst="$(readlink -f "${DISTDIR}/${dst}")"

Looks like you are hardcoding hacks for implementation details which
indicates whatever you're doing is a very bad idea, and is going to fail
whenever the implementation is subtly different than what you've worked
around so far.

>               ln -s "${dst}" "${T}/bazel-distdir/${src}" || die
>       done <<< "$(sed -re 's/!?[A-Za-z]+\?\s+\(\s*//g; s/\s+\)//g' <<< "$@")"

Please don't use horribly unreadable sed expressions.  This just means
that whoever will have to touch this eclass in the future will wish you
were never recruited.

> }
> 
> _BAZEL_ECLASS=1
> fi
> 
> 
> 

-- 
Best regards,
Michał Górny

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to