fn generate_target_rules()

in src/buckify.rs [149:469]


fn generate_target_rules<'scope>(
    context: &'scope RuleContext<'scope>,
    pkg: &'scope Manifest,
    tgt: &'scope ManifestTarget,
) -> Result<(Vec<Rule>, Vec<&'scope Manifest>)> {
    let RuleContext {
        config,
        paths,
        index,
        ..
    } = context;

    log::info!("Generating rules for package {} target {}", pkg, tgt.name);

    let fixups = Fixups::new(config, paths, index, pkg, tgt)?;

    if fixups.omit_target() {
        return Ok((vec![], vec![]));
    }

    log::debug!("pkg {} target {} fixups {:#?}", pkg, tgt.name, fixups);

    let rootmod = relative_path(&paths.third_party_dir, &tgt.src_path);
    let edition = tgt.edition.unwrap_or(pkg.edition);
    let licenses: BTreeSet<_> = fixups
        .manifestwalk(&config.license_patterns, iter::empty::<&str>(), false)?
        .chain(
            pkg.license_file
                .as_ref()
                .map(|file| relative_path(&paths.third_party_dir, pkg.manifest_dir()).join(file))
                .into_iter(),
        )
        .collect();

    let global_rustc_flags = config.rustc_flags.clone();
    let global_platform_rustc_flags = config.platform_rustc_flags.clone();

    let srcdir = relative_path(pkg.manifest_dir(), tgt.src_path.parent().unwrap());

    // Get a list of the most obvious sources for the crate. This is either a list of
    // filename, or a list of globs.
    // If we're configured to get precise sources and we're using 2018+ edition source, then
    // parse the crate to see what files are actually used.
    let mut srcs = if config.precise_srcs && edition >= Edition::Rust2018 {
        match srcfiles::crate_srcfiles(&tgt.src_path) {
            Ok(srcs) => {
                let srcs = srcs
                    .into_iter()
                    .map(|src| normalize_dotdot(&src.path))
                    .collect::<Vec<_>>();
                log::debug!("crate_srcfiles returned {:#?}", srcs);
                srcs
            }
            Err(err) => {
                log::info!("crate_srcfiles failed: {}", err);
                vec![]
            }
        }
    } else {
        vec![]
    };

    if srcs.is_empty() {
        // If that didn't work out, get srcs the globby way
        srcs = vec![tgt.src_path.parent().unwrap().join("**/*.rs")]
    }

    // Platform-specific rule bits which are common to all platforms
    let mut base = PlatformRustCommon {
        rustc_flags: global_rustc_flags.clone(),
        ..Default::default()
    };
    // Per platform rule bits
    let mut perplat: BTreeMap<PlatformName, PlatformRustCommon> = BTreeMap::new();

    // global_platform_rustc_flags is already in terms of PlatformName, do we can just add them in.
    for (plat, flags) in global_platform_rustc_flags {
        perplat.entry(plat).or_default().rustc_flags.extend(flags)
    }

    unzip_platform(
        &config,
        &mut base,
        &mut perplat,
        |rule, flags| {
            log::debug!("pkg {} target {}: adding flags {:?}", pkg, tgt.name, flags);
            rule.rustc_flags.extend(flags)
        },
        fixups.compute_cmdline(),
    )
    .context("rustc_flags")?;

    unzip_platform(
        &config,
        &mut base,
        &mut perplat,
        |rule, srcs| {
            log::debug!("pkg {} target {}: adding srcs {:?}", pkg, tgt.name, srcs);
            rule.srcs.extend(srcs)
        },
        fixups.compute_srcs(srcs)?,
    )
    .context("srcs")?;

    unzip_platform(
        &config,
        &mut base,
        &mut perplat,
        |rule, map| {
            log::debug!(
                "pkg {} target {}: adding mapped_srcs(gen_srcs) {:?}",
                pkg,
                tgt.name,
                map
            );
            let targets = map
                .into_iter()
                .map(|(rule, path)| (rule.target().to_string(), path));
            rule.mapped_srcs.extend(targets);
        },
        fixups.compute_gen_srcs(&srcdir),
    )
    .context("mapped_srcs(gen_srcs)")?;

    unzip_platform(
        &config,
        &mut base,
        &mut perplat,
        |rule, map| {
            log::debug!(
                "pkg {} target {}: adding mapped_srcs(paths) {:?}",
                pkg,
                tgt.name,
                map
            );
            let paths = map
                .into_iter()
                .map(|(from, to)| (from.display().to_string(), to));
            rule.mapped_srcs.extend(paths);
        },
        fixups.compute_mapped_srcs()?,
    )
    .context("mapped_srcs(paths)")?;

    unzip_platform(
        &config,
        &mut base,
        &mut perplat,
        |rule, features| {
            log::debug!(
                "pkg {} target {}: adding features {:?}",
                pkg,
                tgt.name,
                features
            );
            rule.features.extend(features);
        },
        fixups.compute_features(),
    )
    .context("features")?;

    unzip_platform(
        &config,
        &mut base,
        &mut perplat,
        |rule, env| {
            log::debug!("pkg {} target {}: adding env {:?}", pkg, tgt.name, env);
            rule.env.extend(env);
        },
        fixups.compute_env(),
    )
    .context("env")?;

    // Compute set of dependencies any rule we generate here will need. They will only
    // be emitted if we actually emit some rules below.
    let mut dep_pkgs = Vec::new();
    for (deppkg, dep, alias) in fixups.compute_deps()? {
        if dep.has_platform() {
            // If this is a platform-specific dependency, find the
            // matching supported platform(s) and insert it into the appropriate
            // dependency.
            // If the name is DEFAULT_PLATFORM then just put it in the normal generic deps
            for (name, platform) in &config.platform {
                let is_default = name.is_default();

                log::debug!(
                    "pkg {} target {} dep {:?} platform ({}, {:?}) filter {:?}",
                    pkg,
                    tgt.name,
                    dep,
                    name,
                    platform,
                    dep.filter(platform)
                );

                if dep.filter(platform)? {
                    let dep = dep.clone();

                    if let Some(alias) = alias.clone() {
                        if is_default {
                            // Just use normal deps
                            base.named_deps.insert(alias, dep);
                        } else {
                            perplat
                                .entry(name.clone())
                                .or_default()
                                .named_deps
                                .insert(alias, dep);
                        }
                    } else {
                        if is_default {
                            // Normal deps
                            base.deps.insert(dep);
                        } else {
                            perplat.entry(name.clone()).or_default().deps.insert(dep);
                        }
                    }
                    dep_pkgs.extend(deppkg);
                }
            }
        } else {
            // Otherwise this is not platform-specific and can go into the
            // generic dependencies.
            if let Some(alias) = alias {
                base.named_deps.insert(alias, dep);
            } else {
                base.deps.insert(dep);
            }
            dep_pkgs.extend(deppkg);
        }
    }

    // "link_style" only really applies to binaries, so maintain separate binary base & perplat
    let mut bin_base = base.clone();
    let mut bin_perplat = perplat.clone();

    unzip_platform(
        &config,
        &mut bin_base,
        &mut bin_perplat,
        |rule, link_style| {
            log::debug!("pkg {} target {}: link_style {}", pkg, tgt.name, link_style);
            rule.link_style = Some(link_style);
        },
        fixups.compute_link_style(),
    )
    .context("link_style")?;
    // Standalone binary - binary for a package always takes the package's library as a dependency
    // if there is one
    if let Some(true) = pkg.dependency_target().map(ManifestTarget::kind_lib) {
        bin_base.deps.insert(RuleRef::local(index.rule_name(pkg)));
    }

    // Generate rules appropriate to each kind of crate we want to support
    let rules: Vec<Rule> = if (tgt.kind_lib() && tgt.crate_lib())
        || (tgt.kind_proc_macro() && tgt.crate_proc_macro())
        || (tgt.kind_cdylib() && tgt.crate_cdylib())
    {
        // Library or procmacro
        vec![Rule::Library(RustLibrary {
            common: RustCommon {
                common: Common {
                    name: index.rule_name(pkg),
                    public: index.is_public(pkg),
                    licenses,
                },
                krate: tgt.name.replace('-', "_"),
                rootmod,
                edition,
                base,
                platform: perplat,
            },
            proc_macro: tgt.crate_proc_macro(),
            dlopen_enable: tgt.kind_cdylib() && fixups.python_ext().is_none(),
            python_ext: fixups.python_ext().map(str::to_string),
        })]
    } else if tgt.crate_bin() && tgt.kind_custom_build() {
        // Build script
        let buildscript = RustBinary {
            common: RustCommon {
                common: Common {
                    name: format!("{}-{}", pkg, tgt.name),
                    public: false,
                    licenses: Default::default(),
                },
                krate: tgt.name.replace('-', "_"),
                rootmod,
                edition,
                base: PlatformRustCommon {
                    // don't use fixed ones because it will be a cyclic dependency
                    rustc_flags: global_rustc_flags,
                    ..base
                },
                platform: perplat,
            },
        };
        fixups.emit_buildscript_rules(buildscript, &config)?
    } else if tgt.kind_bin() && tgt.crate_bin() && index.is_public(pkg) {
        vec![Rule::Binary(RustBinary {
            common: RustCommon {
                common: Common {
                    name: format!("{}-{}", index.rule_name(pkg), tgt.name),
                    public: index.is_public(pkg),
                    licenses,
                },
                krate: tgt.name.replace('-', "_"),
                rootmod,
                edition,
                base: bin_base,
                platform: bin_perplat,
            },
        })]
    } else {
        // Ignore everything else for now.
        log::info!("pkg {} target {} Skipping {:?}", pkg, tgt.name, tgt.kind());

        vec![]
    };

    Ok((rules, dep_pkgs))
}