Copy files to the target directory after build

19.4k views Asked by At

Let's assume I have a game with the following directory structure:

/src
/resources
Cargo.toml

I would like cargo build to copy the files in the resources directory and paste them in the same directory as the executable file.

I know it is possible to do this using a custom build script, but this seems to be a common case that deserves special treatment. So the question is: does cargo provide a standard way of copying files to the target directory (using just Cargo.toml)?

2

There are 2 answers

2
Vladimir Matveev On BEST ANSWER

No, it doesn't.

You can move files around with build scripts, but these are run before your crate is built because their sole purpose is to prepare the environment (e.g. compile C libraries and shims).

If you think this is an important feature, you can open a feature request in Cargo issue tracker.

Alternatively, you can write a makefile or a shell script which would forward all arguments to cargo and then copy the directory manually:

#!/bin/bash

DIR="$(dirname "$0")"

if cargo "$@"; then
    [ -d "$DIR/target/debug" ] && cp -r "$DIR/resources" "$DIR/target/debug/resources"
    [ -d "$DIR/target/release" ] && cp -r "$DIR/resources" "$DIR/target/release/resources"
fi

Now you can run cargo like

% ./make.sh build
1
Adam Baxter On

I can't solve this for crates (as the accepted answer says) but for a "single" binary that needed a file to run correctly, this works for me.

use std::env;
use std::path::Path;
use std::path::PathBuf;

fn main() {
    println!("cargo:rerun-if-changed=config.json");
    println!("cargo:warning=Hello from build.rs");
    println!("cargo:warning=CWD is {:?}", env::current_dir().unwrap());
    println!("cargo:warning=OUT_DIR is {:?}", env::var("OUT_DIR").unwrap());
    println!("cargo:warning=CARGO_MANIFEST_DIR is {:?}", env::var("CARGO_MANIFEST_DIR").unwrap());
    println!("cargo:warning=PROFILE is {:?}", env::var("PROFILE").unwrap());

    let output_path = get_output_path();
    println!("cargo:warning=Calculated build path: {}", output_path.to_str().unwrap());

    let input_path = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join("config.json");
    let output_path = Path::new(&output_path).join("config.json");
    let res = std::fs::copy(input_path, output_path);
    println!("cargo:warning={:#?}",res)
}

fn get_output_path() -> PathBuf {
    //<root or manifest path>/target/<profile>/
    let manifest_dir_string = env::var("CARGO_MANIFEST_DIR").unwrap();
    let build_type = env::var("PROFILE").unwrap();
    let path = Path::new(&manifest_dir_string).join("target").join(build_type);
    return PathBuf::from(path);
}

This is a mess and I'm very new to Rust. Improvements welcome.