Rust build script to copy a file to target directory - rust

For my project, I would like to copy the file config.ron that is in the root of my project to the target directory when the project is built. I know that you can use include_str! to add the content of the file to the program at compile time, but I would like the file to stay in the root of the target folder so that it can be edited without the need to recompile the program.
I've currently been trying out a build script to accomplish this but I am have no luck.
use std::process::Command;
use std::env;
fn main() {
let profile = std::env::var("PROFILE").unwrap();
match profile.as_str() {
"debug" => {
Command::new("cmd")
.args(&["copy", "/y"])
.arg(&format!(r#"{}\config.ron"#, env::var("CARGO_MANIFEST_DIR").unwrap()))
.arg(&format!(r#"{}\target\debug"#, env::var("CARGO_MANIFEST_DIR").unwrap()))
.status()
.expect("Copy failed to execute.");
()
},
"release" => {
Command::new("cmd")
.args(&["copy", "/y"])
.arg(&format!(r#"{}\config.ron"#, env::var("CARGO_MANIFEST_DIR").unwrap()))
.arg(&format!(r#"{}\target\release"#, env::var("CARGO_MANIFEST_DIR").unwrap()))
.status()
.expect("Copy failed to execute.");
()
},
_ => (),
}
}
What would be the correct way to get this file copied to the target directory using a build script?

If you really want to do it in a build script, I would go with these ingredients:
OUT_DIR and CARGO_TARGET_DIR
std::fs::copy

Related

How do I execute a Move script on Aptos using the Rust SDK?

I want to execute this Move script, e.g. at sources/top_up.move:
script {
use std::signer;
use aptos_framework::aptos_account;
use aptos_framework::aptos_coin;
use aptos_framework::coin;
fun main(src: &signer, dest: address, desired_balance: u64) {
let src_addr = signer::address_of(src);
let balance = coin::balance<aptos_coin::AptosCoin>(src_addr);
if (balance < desired_balance) {
aptos_account::transfer(src, dest, desired_balance - balance);
};
}
}
This is calling functions on the aptos_coin.move module, which is deployed on chain. What it does isn't so important for this question, but in short, it checks that the balance of the destination account is less than desired_balance, and if so, tops it up to desired_balance.
I can execute this Move script via the CLI easily like this:
aptos move compile
aptos move run-script --compiled-script-path build/MyModule/bytecode_scripts/main.mv
Or even just this:
aptos move run-script --script-path sources/top_up.move
What I want to know is whether I can do this using the Rust SDK?
First, you need to compile the script, as you did above. Imagine you have a project layout like this:
src/
main.rs
move/
Move.toml
sources/
top_up.mv
You would want to go in to move/ and run aptos move compile, like you said above. From there, you can depend on the compiled script in your code (see below).
With that complete, here is a minimal code example demonstrating how to execute a Move script using the Rust SDK.
Cargo.toml:
[package]
name = "my-example"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1"
aptos-sdk = { git = "https://github.com/aptos-labs/aptos-core", branch = "mainnet" }
src/main.rs:
use aptos_sdk::crypto::ed25519::Ed25519PublicKey;
use aptos_sdk::types::transaction::authenticator::AuthenticationKey;
use aptos_sdk::{
rest_client::Client,
transaction_builder::TransactionFactory,
types::{
account_address::AccountAddress,
chain_id::ChainId,
transaction::{Script, SignedTransaction, TransactionArgument},
LocalAccount,
},
};
static SCRIPT: &[u8] =
include_bytes!("../../move/build/MyModule/bytecode_scripts/main.mv");
fn main() -> anyhow::Result<()> {
// Prior to the follow code we assume you've already acquired the necessary
// information such as chain_id, the private key of the account submitting
// the transaction, arguments for the Move script, etc.
// Build a transaction factory.
let txn_factory = TransactionFactory::new(chain_id);
// Build a local representation of an account.
let account = LocalAccount::new(
AuthenticationKey::ed25519(&Ed25519PublicKey::from(&private_key)).derived_address()
private_key,
0,
);
// Build an API client.
let client = Client::new("https://fullnode.mainnet.aptoslabs.com");
// Create a builder where the payload is the script.
let txn_builder = transaction_factory.script(Script::new(
SCRIPT.to_vec(),
// type args
vec![],
// args
vec![
TransactionArgument::Address(dest_address),
TransactionArgument::U64(desired_balance),
],
)));
// Build the transaction request and sign it.
let signed_txn = account.sign_with_transaction_builder(
txn_builder
);
// Submit the transaction.
client.submit_and_wait_bcs(&signed_transaction).await?;
}

What is the best way to check if an executable exists for `std::process::Command`?

I want to create a std::process::Command and check whether an executable actually exists at that location before actually spawning the Command later. What would be the pragmatic way to do this?
I have this code:
let c = std::process::Command::new("my_exe");
// something here
c.spawn().unwrap();
I want to be able to validate the my_exe path when creating the Command and then spawn way later.
check whether an executable actually exists at that location before actually spawning the Command.
Don't as that's a time-of-check to time-of-use race condition. Instead, check the error value from executing the Command: Check if a command is in PATH/executable as process.
If you want to check if a file is present in the working directory, you can use Path::exists, as described in How to check whether a path exists?.
Also, Command::spawn() takes a mutable reference to self so c needs to be mutable.
use std::path::Path;
use std::process::Command;
if Path::new("PATH_TO_EXECUTABLE").exists() {
let mut c = Command::new("PATH_TO_EXECUTABLE");
// do something
c.spawn().unwrap();
}
Use the is_executable crate:
use is_executable::IsExecutable;
use std::fs;
let paths = fs::read_dir("./").unwrap();
for path in paths {
let file_path = path.unwrap().path();
if file_path.is_executable() {
println!("File: {:?} is executable!", file_path);
} else {
println!("File: {:?} is _not_ executable!", file_path);
}
}

How to read file from another(nested) folder?

This is my folder structure.
src
├── main.rs
└── assembly_files
└── toyRISC.asm
Why is this not enough or where is the error?
I tried with raw &str:
let filename = "./assembly_files/toyRISC.asm";
let result = fs::read_to_string(path).expect("Please provide valid file name");
Also tried using Path and OsStr , shown in code below.
// main.rs
use std::{ffi::OsStr, fs, path::Path};
fn main() {
let filename = "assembly_files/toyRISC.asm";
let path = Path::new("./assembly_files/toyRISC.asm");
println!("{:?}", path);
let result = fs::read_to_string(path).expect("Please provide valid file name");
}
This is the error
thread 'main' panicked at 'Please provide valid file name: Os { code: 3, kind: NotFound, message: "The system cannot find the path specified."
}', src\main.rs:6:43
When you create a relative Path to a file, e.g. Path::new("./assembly_files/toyRISC.asm"); the path is relative to the folder from which you call the executable. Note that this isn't necessarily the folder where the executable is stored!
If you simply run cargo run at the root of your project to run it, then you need to change the path to Path::new("./src/assembly_files/toyRISC.asm");

How to create a folder outside the poject directory in rust

I using rust to create a folder in ~, however when my code runs the directory is created inside my project's folder instead in ~.
My code:
use std::fs::create_dir_all;
use std::path::Path;
fn main() {
let path = Path::new("~/.hidden_folder");
match create_dir_all(path) {
Ok(f) => {
println!("created folder")
},
Err(err) => {
println!("{:?}", err);
}
};
}
Any idea how to create the folder in the correct directory ?
If you want your home directory, I recommend using this crate or specifying the absolute path. If you want to save it in any other directories, just use relative or absolute paths, but don't use ~ because Rust doesn't know the context of ~.

How to use ExtUtils::MakeMaker to install data in a custom directory

I need to install my main script (as executable) in /usr/bin (depends on the INSTALL_BASE). That part is done.
The next one is to install into the directory /usr/share/project_name/data options.txt, this one is hard for me, see my codes:
Manifest file:
data/options.txt
script/joel-perl
Makefile.PL
Makefile.PL
use ExtUtils::MakeMaker;
WriteMakefile (
NAME => 'joelperl',
VERSION_FROM => 'script/joel-perl',
PREREQ_PM => {
'Switch' => 0
},
EXE_FILES => ['script/joel-perl'],
PM => {
'data/options.txt' => '$(INSTALL_BASE)/share/project_name/data'
}
);
When running:
perl Makefile.PL INSTALL_BASE="/usr"
make
I get "ERROR: Can't create '/usr/share/project_name'", so my question is:
How to add/copy files to an especific place, like in my case?
See File::ShareDir::Install module:
use ExtUtils::MakeMaker;
use File::ShareDir::Install;
install_share 'data';
WriteMakefile(...);
package MY;
use File::ShareDir::Install qw(postamble);

Resources