Browse Source

cli: Use OS-agnostic paths (#3307)

acheron 1 year ago
parent
commit
5df7fbda90
3 changed files with 106 additions and 61 deletions
  1. 1 0
      CHANGELOG.md
  2. 28 14
      cli/src/config.rs
  3. 77 47
      cli/src/lib.rs

+ 1 - 0
CHANGELOG.md

@@ -82,6 +82,7 @@ The minor version will be incremented upon a breaking change and the patch versi
 - lang: Fix using non-instruction composite accounts with `declare_program!` ([#3290](https://github.com/coral-xyz/anchor/pull/3290)).
 - idl: Fix instructions with tuple parameters not producing an error([#3294](https://github.com/coral-xyz/anchor/pull/3294)).
 - ts: Update `engines.node` to `>= 17` ([#3301](https://github.com/coral-xyz/anchor/pull/3301)).
+- cli: Use OS-agnostic paths ([#3307](https://github.com/coral-xyz/anchor/pull/3307)).
 
 ### Breaking
 

+ 28 - 14
cli/src/config.rs

@@ -243,7 +243,10 @@ impl WithPath<Config> {
             let cargo = Manifest::from_path(path.join("Cargo.toml"))?;
             let lib_name = cargo.lib_name()?;
 
-            let idl_filepath = format!("target/idl/{lib_name}.json");
+            let idl_filepath = Path::new("target")
+                .join("idl")
+                .join(&lib_name)
+                .with_extension("json");
             let idl = fs::read(idl_filepath)
                 .ok()
                 .map(|bytes| serde_json::from_reader(&*bytes))
@@ -257,7 +260,10 @@ impl WithPath<Config> {
             });
         }
         for (lib_name, path) in self.get_solidity_program_list()? {
-            let idl_filepath = format!("target/idl/{lib_name}.json");
+            let idl_filepath = Path::new("target")
+                .join("idl")
+                .join(&lib_name)
+                .with_extension("json");
             let idl = fs::read(idl_filepath)
                 .ok()
                 .map(|bytes| serde_json::from_reader(&*bytes))
@@ -1131,7 +1137,7 @@ impl From<_Validator> for Validator {
             url: _validator.url,
             ledger: _validator
                 .ledger
-                .unwrap_or_else(|| DEFAULT_LEDGER_PATH.to_string()),
+                .unwrap_or_else(|| get_default_ledger_path().display().to_string()),
             limit_ledger_size: _validator.limit_ledger_size,
             rpc_port: _validator
                 .rpc_port
@@ -1169,7 +1175,10 @@ impl From<Validator> for _Validator {
     }
 }
 
-pub const DEFAULT_LEDGER_PATH: &str = ".anchor/test-ledger";
+pub fn get_default_ledger_path() -> PathBuf {
+    Path::new(".anchor").join("test-ledger")
+}
+
 const DEFAULT_BIND_ADDRESS: &str = "0.0.0.0";
 
 impl Merge for _Validator {
@@ -1282,12 +1291,12 @@ impl Program {
 
     // Lazily initializes the keypair file with a new key if it doesn't exist.
     pub fn keypair_file(&self) -> Result<WithPath<File>> {
-        let deploy_dir_path = "target/deploy/";
-        fs::create_dir_all(deploy_dir_path)
-            .with_context(|| format!("Error creating directory with path: {deploy_dir_path}"))?;
+        let deploy_dir_path = Path::new("target").join("deploy");
+        fs::create_dir_all(&deploy_dir_path)
+            .with_context(|| format!("Error creating directory with path: {deploy_dir_path:?}"))?;
         let path = std::env::current_dir()
             .expect("Must have current dir")
-            .join(format!("target/deploy/{}-keypair.json", self.lib_name));
+            .join(deploy_dir_path.join(format!("{}-keypair.json", self.lib_name)));
         if path.exists() {
             return Ok(WithPath::new(
                 File::open(&path)
@@ -1303,11 +1312,10 @@ impl Program {
     }
 
     pub fn binary_path(&self, verifiable: bool) -> PathBuf {
-        let path = if verifiable {
-            format!("target/verifiable/{}.so", self.lib_name)
-        } else {
-            format!("target/deploy/{}.so", self.lib_name)
-        };
+        let path = Path::new("target")
+            .join(if verifiable { "verifiable" } else { "deploy" })
+            .join(&self.lib_name)
+            .with_extension("so");
 
         std::env::current_dir()
             .expect("Must have current dir")
@@ -1389,7 +1397,13 @@ macro_rules! home_path {
 
         impl Default for $my_struct {
             fn default() -> Self {
-                $my_struct(home_dir().unwrap().join($path).display().to_string())
+                $my_struct(
+                    home_dir()
+                        .unwrap()
+                        .join($path.replace('/', std::path::MAIN_SEPARATOR_STR))
+                        .display()
+                        .to_string(),
+                )
             }
         }
 

+ 77 - 47
cli/src/lib.rs

@@ -1,7 +1,7 @@
 use crate::config::{
-    AnchorPackage, BootstrapMode, BuildConfig, Config, ConfigOverride, Manifest, ProgramArch,
-    ProgramDeployment, ProgramWorkspace, ScriptsConfig, TestValidator, WithPath,
-    DEFAULT_LEDGER_PATH, SHUTDOWN_WAIT, STARTUP_WAIT,
+    get_default_ledger_path, AnchorPackage, BootstrapMode, BuildConfig, Config, ConfigOverride,
+    Manifest, ProgramArch, ProgramDeployment, ProgramWorkspace, ScriptsConfig, TestValidator,
+    WithPath, SHUTDOWN_WAIT, STARTUP_WAIT,
 };
 use anchor_client::Cluster;
 use anchor_lang::idl::{IdlAccount, IdlInstruction, ERASED_AUTHORITY};
@@ -1028,7 +1028,8 @@ fn init(
     }
 
     // Build the migrations directory.
-    fs::create_dir_all("migrations")?;
+    let migrations_path = Path::new("migrations");
+    fs::create_dir_all(migrations_path)?;
 
     let license = get_npm_init_license()?;
 
@@ -1038,8 +1039,7 @@ fn init(
         let mut package_json = File::create("package.json")?;
         package_json.write_all(rust_template::package_json(jest, license).as_bytes())?;
 
-        let mut deploy = File::create("migrations/deploy.js")?;
-
+        let mut deploy = File::create(migrations_path.join("deploy.js"))?;
         deploy.write_all(rust_template::deploy_script().as_bytes())?;
     } else {
         // Build typescript config
@@ -1049,7 +1049,7 @@ fn init(
         let mut ts_package_json = File::create("package.json")?;
         ts_package_json.write_all(rust_template::ts_package_json(jest, license).as_bytes())?;
 
-        let mut deploy = File::create("migrations/deploy.ts")?;
+        let mut deploy = File::create(migrations_path.join("deploy.ts"))?;
         deploy.write_all(rust_template::ts_deploy_script().as_bytes())?;
     }
 
@@ -1171,7 +1171,11 @@ pub type Files = Vec<(PathBuf, String)>;
 /// ```
 pub fn create_files(files: &Files) -> Result<()> {
     for (path, content) in files {
-        let path = Path::new(path);
+        let path = path
+            .display()
+            .to_string()
+            .replace('/', std::path::MAIN_SEPARATOR_STR);
+        let path = Path::new(&path);
         if path.exists() {
             continue;
         }
@@ -1228,7 +1232,7 @@ pub fn expand(
     let cfg_parent = workspace_cfg.path().parent().expect("Invalid Anchor.toml");
     let cargo = Manifest::discover()?;
 
-    let expansions_path = cfg_parent.join(".anchor/expanded-macros");
+    let expansions_path = cfg_parent.join(".anchor").join("expanded-macros");
     fs::create_dir_all(&expansions_path)?;
 
     match cargo {
@@ -1347,13 +1351,13 @@ pub fn build(
 
     let idl_out = match idl {
         Some(idl) => Some(PathBuf::from(idl)),
-        None => Some(cfg_parent.join("target/idl")),
+        None => Some(cfg_parent.join("target").join("idl")),
     };
     fs::create_dir_all(idl_out.as_ref().unwrap())?;
 
     let idl_ts_out = match idl_ts {
         Some(idl_ts) => Some(PathBuf::from(idl_ts)),
-        None => Some(cfg_parent.join("target/types")),
+        None => Some(cfg_parent.join("target").join("types")),
     };
     fs::create_dir_all(idl_ts_out.as_ref().unwrap())?;
 
@@ -1563,9 +1567,10 @@ fn build_cwd_verifiable(
 ) -> Result<()> {
     // Create output dirs.
     let workspace_dir = cfg.path().parent().unwrap().canonicalize()?;
-    fs::create_dir_all(workspace_dir.join("target/verifiable"))?;
-    fs::create_dir_all(workspace_dir.join("target/idl"))?;
-    fs::create_dir_all(workspace_dir.join("target/types"))?;
+    let target_dir = workspace_dir.join("target");
+    fs::create_dir_all(target_dir.join("verifiable"))?;
+    fs::create_dir_all(target_dir.join("idl"))?;
+    fs::create_dir_all(target_dir.join("types"))?;
     if !&cfg.workspace.types.is_empty() {
         fs::create_dir_all(workspace_dir.join(&cfg.workspace.types))?;
     }
@@ -1595,12 +1600,20 @@ fn build_cwd_verifiable(
             let idl = generate_idl(cfg, skip_lint, no_docs, &cargo_args)?;
             // Write out the JSON file.
             println!("Writing the IDL file");
-            let out_file = workspace_dir.join(format!("target/idl/{}.json", idl.metadata.name));
+            let out_file = workspace_dir
+                .join("target")
+                .join("idl")
+                .join(&idl.metadata.name)
+                .with_extension("json");
             write_idl(&idl, OutFile::File(out_file))?;
 
             // Write out the TypeScript type.
             println!("Writing the .ts file");
-            let ts_file = workspace_dir.join(format!("target/types/{}.ts", idl.metadata.name));
+            let ts_file = workspace_dir
+                .join("target")
+                .join("types")
+                .join(&idl.metadata.name)
+                .with_extension("ts");
             fs::write(&ts_file, idl_ts(&idl)?)?;
 
             // Copy out the TypeScript type.
@@ -1802,7 +1815,12 @@ fn docker_build_bpf(
     println!("Copying out the build artifacts");
     let out_file = cfg_parent
         .canonicalize()?
-        .join(format!("target/verifiable/{binary_name}.so"))
+        .join(
+            Path::new("target")
+                .join("verifiable")
+                .join(&binary_name)
+                .with_extension("so"),
+        )
         .display()
         .to_string();
 
@@ -2053,8 +2071,10 @@ fn verify(
         .path()
         .parent()
         .ok_or_else(|| anyhow!("Unable to find workspace root"))?
-        .join("target/verifiable/")
-        .join(format!("{binary_name}.so"));
+        .join("target")
+        .join("verifiable")
+        .join(&binary_name)
+        .with_extension("so");
 
     let url = cluster_url(&cfg, &cfg.test_validator);
     let bin_ver = verify_bin(program_id, &bin_path, &url)?;
@@ -3417,7 +3437,8 @@ fn validator_flags(
             idl.address = address;
 
             // Persist it.
-            let idl_out = PathBuf::from("target/idl")
+            let idl_out = Path::new("target")
+                .join("idl")
                 .join(&idl.metadata.name)
                 .with_extension("json");
             write_idl(idl, OutFile::File(idl_out))?;
@@ -3547,20 +3568,24 @@ fn validator_flags(
 }
 
 fn stream_logs(config: &WithPath<Config>, rpc_url: &str) -> Result<Vec<std::process::Child>> {
-    let program_logs_dir = ".anchor/program-logs";
-    if Path::new(program_logs_dir).exists() {
-        fs::remove_dir_all(program_logs_dir)?;
+    let program_logs_dir = Path::new(".anchor").join("program-logs");
+    if program_logs_dir.exists() {
+        fs::remove_dir_all(&program_logs_dir)?;
     }
-    fs::create_dir_all(program_logs_dir)?;
+    fs::create_dir_all(&program_logs_dir)?;
+
     let mut handles = vec![];
     for program in config.read_all_programs()? {
-        let idl = fs::read(format!("target/idl/{}.json", program.lib_name))?;
+        let idl_path = Path::new("target")
+            .join("idl")
+            .join(&program.lib_name)
+            .with_extension("json");
+        let idl = fs::read(idl_path)?;
         let idl = convert_idl(&idl)?;
 
-        let log_file = File::create(format!(
-            "{}/{}.{}.log",
-            program_logs_dir, idl.address, program.lib_name,
-        ))?;
+        let log_file = File::create(
+            program_logs_dir.join(format!("{}.{}.log", idl.address, program.lib_name)),
+        )?;
         let stdio = std::process::Stdio::from(log_file);
         let child = std::process::Command::new("solana")
             .arg("logs")
@@ -3574,7 +3599,8 @@ fn stream_logs(config: &WithPath<Config>, rpc_url: &str) -> Result<Vec<std::proc
     if let Some(test) = config.test_validator.as_ref() {
         if let Some(genesis) = &test.genesis {
             for entry in genesis {
-                let log_file = File::create(format!("{}/{}.log", program_logs_dir, entry.address))?;
+                let log_file =
+                    File::create(program_logs_dir.join(&entry.address).with_extension("log"))?;
                 let stdio = std::process::Stdio::from(log_file);
                 let child = std::process::Command::new("solana")
                     .arg("logs")
@@ -3692,10 +3718,9 @@ fn test_validator_file_paths(test_validator: &Option<TestValidator>) -> Result<(
         Some(TestValidator {
             validator: Some(validator),
             ..
-        }) => &validator.ledger,
-        _ => DEFAULT_LEDGER_PATH,
+        }) => PathBuf::from(&validator.ledger),
+        _ => get_default_ledger_path(),
     };
-    let ledger_path = Path::new(ledger_path);
 
     if !ledger_path.is_relative() {
         // Prevent absolute paths to avoid someone using / or similar, as the
@@ -3704,15 +3729,13 @@ fn test_validator_file_paths(test_validator: &Option<TestValidator>) -> Result<(
         std::process::exit(1);
     }
     if ledger_path.exists() {
-        fs::remove_dir_all(ledger_path)?;
+        fs::remove_dir_all(&ledger_path)?;
     }
 
-    fs::create_dir_all(ledger_path)?;
+    fs::create_dir_all(&ledger_path)?;
 
-    Ok((
-        ledger_path.to_owned(),
-        ledger_path.join("test-ledger-log.txt"),
-    ))
+    let log_path = ledger_path.join("test-ledger-log.txt");
+    Ok((ledger_path, log_path))
 }
 
 fn cluster_url(cfg: &Config, test_validator: &Option<TestValidator>) -> String {
@@ -3825,7 +3848,8 @@ fn deploy(
                 idl.address = program_id.to_string();
 
                 // Persist it.
-                let idl_out = PathBuf::from("target/idl")
+                let idl_out = Path::new("target")
+                    .join("idl")
                     .join(&idl.metadata.name)
                     .with_extension("json");
                 write_idl(idl, OutFile::File(idl_out))?;
@@ -4267,12 +4291,14 @@ fn run(cfg_override: &ConfigOverride, script: String, script_args: Vec<String>)
 }
 
 fn login(_cfg_override: &ConfigOverride, token: String) -> Result<()> {
-    let dir = shellexpand::tilde("~/.config/anchor");
-    if !Path::new(&dir.to_string()).exists() {
-        fs::create_dir(dir.to_string())?;
+    let anchor_dir = Path::new(&*shellexpand::tilde("~"))
+        .join(".config")
+        .join("anchor");
+    if !anchor_dir.exists() {
+        fs::create_dir(&anchor_dir)?;
     }
 
-    std::env::set_current_dir(dir.to_string())?;
+    std::env::set_current_dir(&anchor_dir)?;
 
     // Freely overwrite the entire file since it's not used for anything else.
     let mut file = File::create("credentials")?;
@@ -4467,8 +4493,11 @@ fn registry_api_token(_cfg_override: &ConfigOverride) -> Result<String> {
     struct Credentials {
         registry: Registry,
     }
-    let filename = shellexpand::tilde("~/.config/anchor/credentials");
-    let mut file = File::open(filename.to_string())?;
+    let filename = Path::new(&*shellexpand::tilde("~"))
+        .join(".config")
+        .join("anchor")
+        .join("credentials");
+    let mut file = File::open(filename)?;
     let mut contents = String::new();
     file.read_to_string(&mut contents)?;
 
@@ -4730,7 +4759,8 @@ fn get_node_dns_option() -> Result<&'static str> {
 // of spaces in keypair/binary paths, but this should be fixed in the Solana CLI
 // and removed here.
 fn strip_workspace_prefix(absolute_path: String) -> String {
-    let workspace_prefix = std::env::current_dir().unwrap().display().to_string() + "/";
+    let workspace_prefix =
+        std::env::current_dir().unwrap().display().to_string() + std::path::MAIN_SEPARATOR_STR;
     absolute_path
         .strip_prefix(&workspace_prefix)
         .unwrap_or(&absolute_path)