Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read compiler configurations from toml #1347

Merged
merged 13 commits into from
Jun 23, 2023

Conversation

salaheldinsoliman
Copy link
Contributor

1- Implement taking solang's configurations from toml file. solang compile --configuration-file.
2- Implement solang new. a command that creates a new directory with a flipper example as well as the corresponding toml file. solang new --target substrate.

Copy link
Contributor

@xermicus xermicus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Gave it a first review round. Looks quite good already

llvm-IR_optimization_level = "default" # Set llvm optimizer level. Valid options are "none", "less", "default", "aggressive"

[compiler-output]
#verbose = false # show debug messages
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think all of them need to be commented out

llvm-IR_optimization_level = "default" # Set llvm optimizer level. Valid options are "none", "less", "default", "aggressive"

[compiler-output]
#verbose = false # show debug messages
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

@@ -35,6 +36,18 @@ pub enum Commands {

#[command(about = "Generate Solidity interface files from Anchor IDL files")]
Idl(IdlCommand),

#[command(about = "Create a new project with a ready example")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[command(about = "Create a new project with a ready example")]
#[command(about = "Create a new Solang project")]

#[arg(name = "TARGETNAME",required= true, long = "target", value_parser = ["solana", "substrate", "evm"], help = "Target to build for [possible values: solana, substrate]", num_args = 1, hide_possible_values = true)]
pub target_name: String,

#[arg(name = "INPUT", help = "project name", num_args = 1, value_parser = ValueParser::os_string())]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[arg(name = "INPUT", help = "project name", num_args = 1, value_parser = ValueParser::os_string())]
#[arg(name = "INPUT", help = "Name of the project", num_args = 1, value_parser = ValueParser::os_string())]

pub struct Compile {
#[arg(name = "CONFFILE", help = "Take arguments from configuration file", long = "configuration-file", exclusive = true, value_parser = ValueParser::os_string(), required_unless_present = "INPUT", num_args = 0..=1, default_missing_value = "Solang.toml")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to no use a capital S in the default file

Suggested change
#[arg(name = "CONFFILE", help = "Take arguments from configuration file", long = "configuration-file", exclusive = true, value_parser = ValueParser::os_string(), required_unless_present = "INPUT", num_args = 0..=1, default_missing_value = "Solang.toml")]
#[arg(name = "CONFFILE", help = "Take arguments from configuration file", long = "configuration-file", exclusive = true, value_parser = ValueParser::os_string(), required_unless_present = "INPUT", num_args = 0..=1, default_missing_value = "solang.toml")]

pub struct Compile {
#[arg(name = "CONFFILE", help = "Take arguments from configuration file", long = "configuration-file", exclusive = true, value_parser = ValueParser::os_string(), required_unless_present = "INPUT", num_args = 0..=1, default_missing_value = "Solang.toml")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can just name this flag --config (or config-file) to keep it shorter?

"ast-dot"|"cfg"|"llvm-ir"|"llvm-bc" |"object"| "asm" => {
Ok(Some(value))
},
_ => {Err(serde::de::Error::custom("Invalid option for `emit`. Valid options are: `ast-dot`, `cfg`, `llvm-ir`, `llvm-bc`, `object`, `asm`"))}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not seem to be formatted correctly

src/bin/cli/test.rs Show resolved Hide resolved
src/bin/solang.rs Show resolved Hide resolved
let name = if let Some(name) = &target_arg.name {
name
} else {
eprintln!("error: must specifiy target name");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
eprintln!("error: must specifiy target name");
eprintln!("error: compilation target not specified");

}

pub(crate) fn target_arg(target_arg: &TargetArg) -> Target {
if target_arg.name.as_str() == "solana" || target_arg.name.as_str() == "evm" {
let name = if let Some(name) = &target_arg.name {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest calling this variable target_name.

}
}

fn emit_de<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function deserves a better name.

Comment on lines +383 to +590
fn default_true() -> bool {
true
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per this PR iddm/serde-aux#23, you don't need this function. You could just write default_true.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for some reason, bool_true doesn't work

package_toml = r#"
contracts = ["flipper"] # Contracts to include from the compiled files
import_path = ["path1", "path2"]
import_map = ["map1=path", "map2=path2"] # Maps to import. Define as : import_paths = ["map=path/to/map", "map2=path/to/map2", ..]"#;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other commands like target, optimizations and emit could also be tested.

}

#[derive(Args)]
pub struct New {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This struct deserves a better name. New is too generic.

Copy link
Contributor

@xermicus xermicus Jun 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree on this one, I don't see how this could not be very obvious that it's the struct for the new subcommand, given the struct is in the cli module. Additionally I think it's confusing if the struct name diverges from the subcommand name

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might make sense here, but fn new(args: New) in line 70 from src/bin/solang.rs deserves either another name or a doc comment. I suggested a new name here because of this awkward construct.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm yeah that's a good point, the new function should definitively be renamed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be called init, just like git init?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cargo allows cargo init and cargo new. git new does not exist

Copy link
Contributor Author

@salaheldinsoliman salaheldinsoliman Jun 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Signed-off-by: salaheldinsoliman <[email protected]>
Copy link
Contributor

@xermicus xermicus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great work, thanks! ❤️

Can you please add a short section in the docs about this?

What readers might be curios about is what the default values look like. E.g. if you have an empty TOML or empty sections in the TOML.

Also my gut feeling is that many users want to depend on some openzepplin contracts, would be nice if the docs would show how to use importmaps in the TOML file.

Cargo.toml Outdated
@@ -60,6 +60,7 @@ scale-info = "2.4"
petgraph = "0.6.3"
wasmparser = "0.106.0"
wasm-encoder = "0.28"
toml = "0.7.3"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.7.4 is the latest. It might be better to use "0.7" because we are unlikely to care about the patch level

input_files = ["flipper.sol"] # Files to be compiled. You can define multiple files as : input_files = ["file1", "file2", ..]
contracts = ["flipper"] # Contracts to include from the compiled files
import_path = []
import_map = [] # Maps to import. Define as : import_paths = ["map=path/to/map", "map2=path/to/map2", ..]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an unnatural way of specifying a map in toml. How about

[package]
[import_map]
map1 = "path/to/map"
map2 = "path/to/other/map"

}

#[derive(Args)]
pub struct New {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be called init, just like git init?

}

#[derive(Args)]
pub struct New {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cargo allows cargo init and cargo new. git new does not exist

@@ -127,10 +261,22 @@ pub struct TargetArg {
pub value_length: Option<u64>,
}

#[derive(Args, Deserialize, Debug, PartialEq)]
pub struct CompileTargetArg {
#[arg(name = "TARGET", required_unless_present = "CONFFILE" ,long = "target", value_parser = ["solana", "substrate", "evm"], help = "Target to build for [possible values: solana, substrate]", num_args = 1, hide_possible_values = true)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we need required_unless_present = "CONFFILE" here?

#[derive(Args)]
#[derive(Args, Deserialize, Debug, PartialEq)]
pub struct CompilePackage {
#[arg(name = "INPUT", help = "Solidity input files",value_parser = ValueParser::path_buf(), num_args = 1.., required_unless_present = "CONFFILE")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as my comment above. Then, given a solang.toml file is present, we can just do solang compile (akin to how you can just cargo build in Rust projects). Which I'd find really neat

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good pointer, Thanks

Signed-off-by: salaheldinsoliman <[email protected]>
docs/running.rst Outdated
solang new --target solana my_project

Start a new Solang project with an example `flipper.sol <https://github.com/hyperledger/solang/blob/main/examples/solana/flipper.sol>`_ contract,
next to an example configuration file.
Copy link
Contributor

@xermicus xermicus Jun 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
next to an example configuration file.
and a default configuration file.

docs/running.rst Outdated
Comment on lines 164 to 174
Starting a new project
______________________________


solang new --target solana my_project

Start a new Solang project with an example `flipper.sol <https://github.com/hyperledger/solang/blob/main/examples/solana/flipper.sol>`_ contract,
next to an example configuration file.



Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Paragraph needs to explain the user in more detail what exactly a "Solang project" is (that it creates a new directory with a default config file is just the product of this command, but we also need to explain the purpose of these Projects)

Comment on lines +25 to +26
llvm-IR-optimization-level = "default" # Set llvm optimizer level. Valid options are "none", "less", "default", "aggressive"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIY I just merged #1365, could you include that here too?

Comment on lines 89 to 95
match create_dir(&dir_path) {
Ok(_) => (),
Err(error) => {
eprintln!("couldn't create project directory, reason: {error}");
exit(1)
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
match create_dir(&dir_path) {
Ok(_) => (),
Err(error) => {
eprintln!("couldn't create project directory, reason: {error}");
exit(1)
}
};
if let Err(error) = create_dir(&dir_path) {
eprintln!("couldn't create project directory, reason: {error}");
exit(1);
}


impl PackageTrait for CompilePackage {
fn get_input(&self) -> &Vec<PathBuf> {
match &self.input {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use map_err instead of the match here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think .expect() would look better

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Panicking is not the same as std::process::exit(1). While technically the result is almost the same (I'm not sure whether we care about the different exit code). An unwrap or expect implies that this is a situation we do not anticipate for, and if it should ever happen regardless there's nothing to do about it. Here we can terminate in a more graceful way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but a map_err would imply converting the Option<> to a Result<> beforehand. I think an if let Some() would suffice, what do you think?

Copy link
Contributor

@xermicus xermicus Jun 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I didn't pay attention close enough, sorry. Yes you could return inside the if let and exit afterwards. Or leave it like it is, it's not terribly important

@@ -218,10 +449,50 @@ pub(crate) fn target_arg(target_arg: &TargetArg) -> Target {
target
}

pub fn imports_arg(package: &Package) -> FileResolver {
pub trait PackageTrait {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a comment explaining the trait and what it's used for

src/bin/solang.rs Show resolved Hide resolved
docs/running.rst Outdated
name = "solana" # Target name


Fields that are not explicitly present in the .toml file are defaulted. If any other argument is provided in the command line, for example, ``solang compile --config-file --target substrate``, the argument will be overridden.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Fields that are not explicitly present in the .toml file are defaulted. If any other argument is provided in the command line, for example, ``solang compile --config-file --target substrate``, the argument will be overridden.
Fields not explicitly present in the .toml acquire the compiler's default value. If any other argument is provided in the command line, for example, ``solang compile --config-file --target substrate``, the argument will be overridden.

docs/running.rst Outdated
name = "solana" # Target name


Fields that are not explicitly present in the .toml file are defaulted. If any other argument is provided in the command line, for example, ``solang compile --config-file --target substrate``, the argument will be overridden.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I need to specify the .toml file I am referring to when I run solang compile --config-file? What happens when I have more than one .toml file in my repository?

If you are creating a .toml with a reserved name, that should be noted in the documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default one is solang.toml , but if a special name is used, it will be taken instead.


/// Constructor that initializes the `bool` value to the given `init_value`.
@payer(payer)
constructor(address payer, bool initvalue) {
Copy link
Contributor

@LucasSte LucasSte Jun 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
constructor(address payer, bool initvalue) {
constructor(bool initvalue) {

There is no need to pass the payer as an argument.

strength-reduce = true
vector-to-slice = true
common-subexpression-elimination = true
llvm-IR-optimization-level = "default" # Set llvm optimizer level. Valid options are "none", "less", "default", "aggressive"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not apply to llvm-IR only, also to target specific machine code. This should be called llvm-optimization-level

Copy link
Contributor

@xermicus xermicus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

docs/running.rst Outdated Show resolved Hide resolved
docs/running.rst Outdated Show resolved Hide resolved
Co-authored-by: Lucas Steuernagel <[email protected]>
Signed-off-by: Cyrill Leutwiler <[email protected]>
@xermicus xermicus merged commit d772fe4 into hyperledger:main Jun 23, 2023
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants