SUMMARY = "Rust compiler and runtime libaries" HOMEPAGE = "http://www.rust-lang.org" SECTION = "devel" LICENSE = "MIT | Apache-2.0" LIC_FILES_CHKSUM ="file://COPYRIGHT;md5=43e1f1fb9c0ee3af66693d8c4fecafa8" inherit rust SRC_URI = "\ https://static.rust-lang.org/dist/rustc-${PV}-src.tar.gz;name=rust \ https://static.rust-lang.org/dist/${RUST_SNAPSHOT}.tar.gz;name=rust-snapshot \ " S = "${WORKDIR}/rustc-${PV}" DEPENDS += "file-native" # We generate local targets, and need to be able to locate them export RUST_TARGET_PATH="${WORKDIR}/targets/" export FORCE_CRATE_HASH="${BB_TASKHASH}" # Right now this is focused on arm-specific tune features. # We get away with this for now as one can only use x86-64 as the build host # (not arm). # Note that TUNE_FEATURES is _always_ refering to the target, so we really # don't want to use this for the host/build. def llvm_features_from_tune(d): f = [] feat = d.getVar('TUNE_FEATURES', True) if not feat: return "" feat = frozenset(feat.split()) if 'vfpv4' in feat: f.append("+vfp4") if 'vfpv3' in feat: f.append("+vfp3") if 'vfpv3d16' in feat: f.append("+d16") if 'vfpv2' in feat or 'vfp' in feat: f.append("+vfp2") if 'neon' in feat: f.append("+neon") if 'aarch64' in feat: f.append("+v8") v7=frozenset(['armv7a', 'armv7r', 'armv7m', 'armv7ve']) if not feat.isdisjoint(v7): f.append("+v7") if 'armv6' in feat: f.append("+v6") if 'dsp' in feat: f.append("+dsp") if d.getVar('ARM_THUMB_OPT', True) is "thumb": if not feat.isdisjoint(v7): f.append("+thumb2") f.append("+thumb-mode") if 'cortexa5' in feat: f.append("+a5") if 'cortexa7' in feat: f.append("+a7") if 'cortexa9' in feat: f.append("+a9") if 'cortexa15' in feat: f.append("+a15") if 'cortexa17' in feat: f.append("+a17") # Seems like it could be infered by the lack of vfp options, but we'll # include it anyhow if 'soft' in feat: f.append("+soft-float") return ','.join(f) # TARGET_CC_ARCH changes from build/cross/target so it'll do the right thing # this should go away when https://github.com/rust-lang/rust/pull/31709 is # stable (1.9.0?) def llvm_features_from_cc_arch(d): f = [] feat = d.getVar('TARGET_CC_ARCH', True) if not feat: return "" feat = frozenset(feat.split()) if '-mmmx' in feat: f.append("+mmx") if '-msse' in feat: f.append("+sse") if '-msse2' in feat: f.append("+sse2") if '-msse3' in feat: f.append("+sse3") if '-mssse3' in feat: f.append("+ssse3") if '-msse4.1' in feat: f.append("+sse4.1") if '-msse4.2' in feat: f.append("+sse4.2") if '-msse4a' in feat: f.append("+sse4a") if '-mavx' in feat: f.append("+avx") if '-mavx2' in feat: f.append("+avx2") return ','.join(f) ## arm-unknown-linux-gnueabihf DATA_LAYOUT[arm] = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64" LLVM_TARGET[arm] = "${RUST_TARGET_SYS}" TARGET_ENDIAN[arm] = "little" TARGET_POINTER_WIDTH[arm] = "32" FEATURES[arm] = "+v6,+vfp2" PRE_LINK_ARGS[arm] = "-Wl,--as-needed" DATA_LAYOUT[aarch64] = "e-m:e-i64:64-i128:128-n32:64-S128" LLVM_TARGET[aarch64] = "aarch64-unknown-linux-gnu" TARGET_ENDIAN[aarch64] = "little" TARGET_POINTER_WIDTH[aarch64] = "64" PRE_LINK_ARGS[aarch64] = "-Wl,--as-needed" ## x86_64-unknown-linux-gnu DATA_LAYOUT[x86_64] = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" LLVM_TARGET[x86_64] = "x86_64-unknown-linux-gnu" TARGET_ENDIAN[x86_64] = "little" TARGET_POINTER_WIDTH[x86_64] = "64" PRE_LINK_ARGS[x86_64] = "-Wl,--as-needed -m64" ## i686-unknown-linux-gnu DATA_LAYOUT[i686] = "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128" LLVM_TARGET[i686] = "i686-unknown-linux-gnu" TARGET_ENDIAN[i686] = "little" TARGET_POINTER_WIDTH[i686] = "32" PRE_LINK_ARGS[i686] = "-Wl,--as-needed -m32" ## XXX: a bit of a hack so qemux86 builds, clone of i686-unknown-linux-gnu above DATA_LAYOUT[i586] = "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128" LLVM_TARGET[i586] = "i586-unknown-linux-gnu" TARGET_ENDIAN[i586] = "little" TARGET_POINTER_WIDTH[i586] = "32" PRE_LINK_ARGS[i586] = "-Wl,--as-needed -m32" TARGET_PRE_LINK_ARGS = "${TARGET_CC_ARCH} ${TOOLCHAIN_OPTIONS}" BUILD_PRE_LINK_ARGS = "${BUILD_CC_ARCH} ${TOOLCHAIN_OPTIONS}" HOST_PRE_LINK_ARGS = "${HOST_CC_ARCH} ${TOOLCHAIN_OPTIONS}" # These LDFLAGS have '-L' options in them. We need these to come last so they # don't screw up the link order and pull in the wrong rust build/version. # TODO: may want to strip out all the '-L' flags entirely here TARGET_POST_LINK_ARGS = "${TARGET_LDFLAGS}" BUILD_POST_LINK_ARGS = "${BUILD_LDFLAGS}" HOST_POST_LINK_ARGS = "${HOST_LDFLAGS}" def arch_for(d, thing): return d.getVar('{}_ARCH'.format(thing), True) def sys_for(d, thing): return d.getVar('{}_SYS'.format(thing), True) def prefix_for(d, thing): return d.getVar('{}_PREFIX'.format(thing), True) ## Note: TOOLCHAIN_OPTIONS is set to "" by native.bbclass and cross.bbclass, ## which prevents us from grabbing them when building a cross compiler (native doesn't matter). ## We workaround this in internal-rust-cross.bbclass. def cflags_for(d, thing): cc_arch = d.getVar('{}_CC_ARCH'.format(thing), True) or "" flags = d.getVar('{}_CFLAGS'.format(thing), True) or "" tc = d.getVar('TOOLCHAIN_OPTIONS', True) or "" return ' '.join([cc_arch, flags, tc]) def cxxflags_for(d, thing): cc_arch = d.getVar('{}_CC_ARCH'.format(thing), True) or "" flags = d.getVar('{}_CXXFLAGS'.format(thing), True) or "" tc = d.getVar('TOOLCHAIN_OPTIONS', True) or "" return ' '.join([cc_arch, flags, tc]) # Convert a normal arch (HOST_ARCH, TARGET_ARCH, BUILD_ARCH, etc) to something # rust's internals won't choke on. def arch_to_rust_target_arch(arch): if arch == "i586" or arch == "i686": return "x86" else: return arch # generates our target CPU value def llvm_cpu(d): cpu = d.getVar('TUNE_PKGARCH', True) target = d.getVar('TRANSLATED_TARGET_ARCH', True) trans = {} trans['corei7-64'] = "corei7" trans['core2-32'] = "core2" trans['x86-64'] = "x86-64" trans['i686'] = "i686" trans['i586'] = "i586" try: return trans[cpu] except: return trans.get(target, "generic") def post_link_args_for(d, thing, arch): post_link_args = (d.getVar('{}_POST_LINK_ARGS'.format(thing), True) or "").split() post_link_args.extend((d.getVarFlag('POST_LINK_ARGS', arch, True) or "").split()) return post_link_args def pre_link_args_for(d, thing, arch): ldflags = (d.getVar('{}_PRE_LINK_ARGS'.format(thing), True) or "").split() ldflags.extend((d.getVarFlag('PRE_LINK_ARGS', arch, True) or "").split()) return ldflags def ldflags_for(d, thing, arch): a = pre_link_args_for(d, thing, arch) a.extend(post_link_args_for(d, thing, arch)) return a TARGET_LLVM_CPU="${@llvm_cpu(d)}" TARGET_LLVM_FEATURES = "${@llvm_features_from_tune(d)} ${@llvm_features_from_cc_arch(d)}" # class-native implies TARGET=HOST, and TUNE_FEATURES only describes the real # (original) target. TARGET_LLVM_FEATURES_class-native = "${@llvm_features_from_cc_arch(d)}" def rust_gen_target(d, thing, wd): import json arch = arch_for(d, thing) sys = sys_for(d, thing) prefix = prefix_for(d, thing) features = "" cpu = "generic" if thing is "TARGET": features = d.getVar('TARGET_LLVM_FEATURES', True) or "" cpu = d.getVar('TARGET_LLVM_CPU', True) features = features or d.getVarFlag('FEATURES', arch, True) or "" features = features.strip() # build tspec tspec = {} tspec['llvm-target'] = d.getVarFlag('LLVM_TARGET', arch, True) tspec['data-layout'] = d.getVarFlag('DATA_LAYOUT', arch, True) tspec['target-pointer-width'] = d.getVarFlag('TARGET_POINTER_WIDTH', arch, True) tspec['target-word-size'] = tspec['target-pointer-width'] tspec['target-endian'] = d.getVarFlag('TARGET_ENDIAN', arch, True) tspec['arch'] = arch_to_rust_target_arch(arch) tspec['os'] = "linux" tspec['env'] = "gnu" tspec['linker'] = "{}{}gcc".format(d.getVar('CCACHE', True), prefix) tspec['objcopy'] = "{}objcopy".format(prefix) tspec['ar'] = "{}ar".format(prefix) tspec['cpu'] = cpu if features is not "": tspec['features'] = features tspec['dynamic-linking'] = True tspec['executables'] = True tspec['morestack'] = True tspec['linker-is-gnu'] = True tspec['has-rpath'] = True tspec['has-elf-tls'] = True tspec['position-independent-executables'] = True tspec['pre-link-args'] = pre_link_args_for(d, thing, arch) tspec['post-link-args'] = post_link_args_for(d, thing, arch) # write out the target spec json file with open(wd + sys + '.json', 'w') as f: json.dump(tspec, f) python do_rust_gen_targets () { wd = d.getVar('WORKDIR', True) + '/targets/' # It is important 'TARGET' is last here so that it overrides our less # informed choices for BUILD & HOST if TARGET happens to be the same as # either of them. for thing in ['BUILD', 'HOST', 'TARGET']: bb.debug(1, "rust_gen_target for " + thing) rust_gen_target(d, thing, wd) } addtask rust_gen_targets after do_patch before do_compile do_rust_gen_targets[dirs] += "${WORKDIR}/targets" def rust_gen_mk_cfg(d, thing): '''' Rust's build system adds support for new archs via 2 things: 1. a file in mk/cfg which defines how the runtime libraries are built 2. and rustc arch definition either built into the compiler or supplied as a .json file This generates a new #1 for the given 'thing' (one of HOST, TARGET, BUILD) using a "similar" config that rust already supplies as a template. Note that the configure process also depends on the existence of #1, so we have to run this before do_configure ''' import subprocess rust_base_sys = rust_base_triple(d, thing) arch = arch_for(d, thing) sys = sys_for(d, thing) prefix = prefix_for(d, thing) llvm_target = d.getVarFlag('LLVM_TARGET', arch, True) ldflags = ' '.join(ldflags_for(d, thing, arch)) b = d.getVar('WORKDIR', True) + '/mk-cfg/' o = open(b + sys_for(d, thing) + '.mk', 'w') i = open(d.getVar('S', True) + '/mk/cfg/' + rust_base_sys + '.mk', 'r') r = subprocess.call(['sed', # CFLAGS, LDFLAGS, CXXFLAGS, CPPFLAGS are used by rust's build for a # wide range of targets (not just HOST). Yocto's settings for them will # be inappropriate, avoid having random targets try to use them, we'll # add as needed. '-e', 's/$(CFLAGS)//', '-e', 's/$(CXXFLAGS)//', '-e', 's/$(CPPFLAGS)//', '-e', 's/$(LDFLAGS)//', # update all triplets to the new one '-e', 's/{}/{}/g'.format(rust_base_sys, sys), # Replace tools with our own (CROSS_PREFIX is appended to all tools # by rust's build system). We delete and then insert this because not # all targets define it. '-e', 's/^CROSS_PREFIX_{}.*$//'.format(sys), '-e', '2 a CROSS_PREFIX_{} := {}'.format(sys, prefix), '-e', 's/^CFG_LLVM_TARGET_.*$//', '-e', '2 a CFG_LLVM_TARGET_{} := {}'.format(sys, llvm_target), '-e', 's/^CC_{}=.*$/CC_{} := gcc/'.format(sys, sys), '-e', 's/^CXX_{}.*$/CXX_{} := g++/'.format(sys, sys), '-e', 's/^CPP_{}.*$/CPP_{} := gcc -E/'.format(sys, sys), '-e', 's/^AR_{}.*$/AR_{} := ar/'.format(sys, sys), # Some targets don't have LINK even though it is required to build. '-e', 's/^LINK_{}.*$//'.format(sys), '-e', '2 a LINK_{} := gcc'.format(sys), # Append our flags to the existing ones '-e', '/^CFG_JEMALLOC_CFLAGS/ s;$; {};'.format(cflags_for(d, thing)), '-e', '/^CFG_GCCISH_CFLAGS/ s;$; {};'.format(cflags_for(d, thing)), '-e', '/^CFG_GCCISH_CXXFLAGS/ s;$; {};'.format(cxxflags_for(d, thing)), '-e', '/^CFG_GCCISH_LINK_FLAGS/ s;$; {};'.format(ldflags), # May need to add: CFG_LLC_FLAGS_{} ], stdout=o, stdin=i) if r: raise Exception o.write("OBJCOPY_{} := {}objcopy\n".format(sys, prefix)) # Note: this isn't how this variable is supposed to be used, but for # non-msvc platforms nothing else touches it. # These are the only extra flags passed to the rustllvm (c++ code) build. # These are only used for host (even though we emit them for all targets) # Without this, there are link failures due to GLIBC_CXX11_ABI issues in # certain setups. o.write("EXTRA_RUSTLLVM_CXXFLAGS_{} := {}\n".format(sys, cxxflags_for(d, thing))) o.close() i.close() python do_rust_arch_fixup () { for thing in ['BUILD', 'HOST', 'TARGET']: bb.debug(1, "rust_gen_mk_cfg for " + thing) rust_gen_mk_cfg(d, thing) } addtask rust_arch_fixup before do_configure after do_patch do_rust_arch_fixup[dirs] += "${WORKDIR}/mk-cfg" llvmdir = "${STAGING_DIR_NATIVE}/${prefix_native}" # prevent the rust-installer scripts from calling ldconfig export CFG_DISABLE_LDCONFIG="notempty" do_configure () { # FIXME: target_prefix vs prefix, see cross.bbclass # FIXME: this path to rustc (via `which rustc`) may not be quite right in the case # where we're reinstalling the compiler. May want to try for a real # path based on bitbake vars # Also will be wrong when relative libdir and/or bindir aren't 'bin' and 'lib'. local_rust_root="$PWD/dl" # - rpath is required otherwise rustc fails to resolve symbols # - submodule management is done by bitbake's fetching ${S}/configure \ "--enable-rpath" \ "--disable-docs" \ "--disable-manage-submodules" \ "--disable-debug" \ "--enable-optimize" \ "--enable-optimize-cxx" \ "--disable-llvm-version-check" \ "--llvm-root=${llvmdir}" \ "--enable-optimize-tests" \ "--release-channel=stable" \ "--prefix=${prefix}" \ "--target=${TARGET_SYS}" \ "--host=${HOST_SYS}" \ "--build=${BUILD_SYS}" \ "--localstatedir=${localstatedir}" \ "--sysconfdir=${sysconfdir}" \ "--datadir=${datadir}" \ "--infodir=${infodir}" \ "--mandir=${mandir}" \ "--libdir=${libdir}" \ "--platform-cfg=${WORKDIR}/mk-cfg/" \ "--enable-local-rust" \ "--local-rust-root=${WORKDIR}/${RUST_SNAPSHOT}/rustc" \ ${EXTRA_OECONF} } rust_runmake () { echo "COMPILE ${PN}" "$@" # CFLAGS, LDFLAGS, CXXFLAGS, CPPFLAGS are used by rust's build for a # wide range of targets (not just TARGET). Yocto's settings for them will # be inappropriate, avoid using. unset CFLAGS unset LDFLAGS unset CXXFLAGS unset CPPFLAGS oe_runmake "VERBOSE=1" "$@" } do_compile () { rust_runmake } rust_do_install () { rust_runmake DESTDIR="${D}" install # Rust's install.sh doesn't mark executables as executable because # we're using a custom bindir, do it ourselves. chmod +x "${D}/${bindir}/rustc" chmod +x "${D}/${bindir}/rustdoc" chmod +x "${D}/${bindir}/rust-gdb" # Install our custom target.json files local td="${D}${libdir}/rustlib/" install -d "$td" for tgt in "${WORKDIR}/targets/"* ; do install -m 0644 "$tgt" "$td" done # Remove any files directly installed into libdir to avoid # conflicts between cross and native rm -f ${D}${libdir}/lib*.so } do_install () { rust_do_install } # ex: sts=4 et sw=4 ts=8