Skip to content

Possibility to set the package version dynamically #6583

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

Open
vorner opened this issue Jan 22, 2019 · 90 comments
Open

Possibility to set the package version dynamically #6583

vorner opened this issue Jan 22, 2019 · 90 comments
Labels
A-manifest Area: Cargo.toml issues C-feature-request Category: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted` S-needs-rfc Status: Needs an RFC to make progress.

Comments

@vorner
Copy link

vorner commented Jan 22, 2019

Maintainer notes


Hello

I'm building a rust application using internal teamcity CI job. I want the build number to be part of the version of the binary (so eg. ./app --version knows from which build it came).

The only way I found so far is to let the build first edit the Cargo.toml (sed -i -e 's/^version = .*/version = "%build.version%"/' Cargo.toml), which seems ugly and fragile.

It would be great if I could somehow override the version from Cargo.toml through the command line ‒ either as a parameter or an environment variable.

Would something like that make sense? Are there plans to support it?

@vorner vorner added the C-feature-request Category: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted` label Jan 22, 2019
@dwijnand
Copy link
Member

I think this could be created as a cargo extension using https://github.com/ordian/toml_edit. You could use https://github.com/killercup/cargo-edit as inspiration.

@vorner
Copy link
Author

vorner commented Jan 22, 2019

Actually, I don't want to modify the Cargo.toml at all if possible, just use that different value of version during a single cargo build. So something like cargo build --config package.version="0.1.2".

But yes, you're right, there are certainly less fragile ways to edit Cargo.toml.

@dwijnand
Copy link
Member

I think there's a ticket for being able to modify options (I guess I'm both Cargo.toml and .cargo/config) via a command, like git config, but I don't know if it extends to temporary overrides.

@mixalturek
Copy link

It's very common use case to specify the version from command line while releasing on a CI server that tracks and auto increments the build number. GNU/make, Maven and Gradle natively support it, manual editing of a build-spec file is not practical and error prone.

make target VERSION=%build.version%
mvn release:prepare -DreleaseVersion=%build.version%
./gradlew build -Pversion=%build.version%

@dwijnand
Copy link
Member

dwijnand commented Jan 23, 2019

mvn release:prepare is based on the maven release plugin. The equivalent is https://github.com/sunng87/cargo-release, and I don't know if it supports specifying the version on the command line.

@vorner
Copy link
Author

vorner commented Jan 24, 2019

I don't think it is equivalent for my use case. This is for releasing on crates.io and modifying Cargo.toml.

What I want is somewhat completely the opposite. I want to leave Cargo.toml as it is, not touch git (tags, push) at all ‒ that happens externally. I want to just build the binary, but the binary should be fed with a version different from the one in Cargo.toml, because for this use case, Cargo.toml is not the authoritative source.

@dwijnand
Copy link
Member

What does

the binary should be fed with a version different from the one in Cargo.toml

mean? Is it just env!("CARGO_PKG_VERSION") (and variants, like CARGO_PKG_VERSION_MAJOR) usage?

@vorner
Copy link
Author

vorner commented Jan 24, 2019

I'm not 100% sure there are no other places the version is passed into the compilation, but I guess so. I haven't tried yet (I might), but I guess cargo will set the env var unconditionally and overwrite it even if I pass it from outside.

@dwijnand
Copy link
Member

Maybe we could just change that: only set the environment var if unset.

@lukaslueg
Copy link
Contributor

built may help your use-case to some extent.

@vorner
Copy link
Author

vorner commented Jan 26, 2019

Looking at built, it seems to be really nice and useful thing and I'll keep this in mind. But I don't think this will help me here ‒ this doesn't help me push the right version into eg. clap or structopt.

@dwijnand
Copy link
Member

Can you not define the version in clap/structopt in terms of built_info::PKG_VERSION?

@vorner
Copy link
Author

vorner commented Jan 26, 2019

By default, they take the version from CARGO_PKG_VERSION. I could set that manually to built_info::PKG_VERSION but I don't see how that would help me, because built also takes the value from there.

@dwijnand
Copy link
Member

You're right, sorry. I think the solution is to not override the environment variables, then.

@Kaiser1989
Copy link

Kaiser1989 commented Nov 9, 2020

Is there any progress? Also looking for the feature to externally manipulate the package version. (CI is setting the version, Cargo.toml should not be changed).

An Env-Variable would be nice:
CARGO_TOML_PACKAGE_VERSION

This could also be done for every other part of the Cargo.toml:

  • CARGO_TOML_PACKAGE_NAME
  • CARGO_TOML_PACKAGE_AUTHORS
  • ...

The place within the code is here:
https://github.com/rust-lang/cargo/blob/master/src/cargo/util/toml/mod.rs#L68
There should be an env-override after parsing the toml and before checking for unused.

This should be analogous to https://doc.rust-lang.org/cargo/reference/config.html#environment-variables

I could add the implementation if we find a good name for env variables...

@mieubrisse
Copy link

Adding another +1 - we're setting the version upon release, and it'd be great to not need to edit Cargo.toml each time

@cgranade
Copy link

By way of another +1 and showing analogous functionality in other toolchains, the .NET SDK allows for overriding version numbers at the command line with commands like dotnet build /p:Version=1.0.1 (https://andrewlock.net/version-vs-versionsuffix-vs-packageversion-what-do-they-all-mean/). I've often found that overriding package versions that way can be useful in using CI pipelines to control how packages are versioned.

@Ri0n
Copy link

Ri0n commented Sep 30, 2021

absolutely same for me. CI and stuff.
I was going to use "built"'s GIT_VERSION for that, but we build in docker containers not passing .git inside. So this info is lost.

@sd-collins
Copy link

This seems to be working on nightly right now: #6699

e.g. cargo build -Z unstable-options --config 'package.version="1.5.2"'

@QAston
Copy link

QAston commented Jan 8, 2022

@Humandoodlebug

cargo build -Z unstable-options --config 'package.version="1.5.2"'

Will not work because --config overrides the cargo configuration, not the build definition in Cargo.toml

@colemickens
Copy link

Editing the Cargo.toml isn't practical in some scenarios anyway - in Nix we don't really have conventional network access during build phases, and so it's not possible rewrite toml and regenerate lock files to workaround this.

I do see that there is a package macro that makes it easy to override the build version, falling back to the Cargo version, but it wouldn't be fun adding this to lots of Rust projects if Cargo could do it: https://docs.rs/git-version/latest/git_version/macro.git_version.html

@Snazzie
Copy link

Snazzie commented Feb 23, 2022

Very surprising that this is still an open issue in feb 2022. 3 years have passed! probably lost/forgotten in the 1.2k issues.
Theres no good way to programmatically replace the version in cargo.toml.

Could we @ some of the active maintainers to get some visibility on this issue?

+1 to adding version override in CLI like dotnet.

@brandonros
Copy link

Is there a way to get cargo build to blend Cargo.toml version with [[bin]] name output? so that the cargo build --release makes a project-name-version.dll for example?

@burdiyan
Copy link

+1 here. I don't understand where the practice of statically specifying a version in a checked in manifest file comes from. I just want to release whatever is already checked in without having to commit again to change any manifests. It's very error prone unless you're using some fancy automated tools. It's so easy to overwrite a version that was already published previously if you release forgetting to change the manifest.

Being able to specify the version in the command line and completely omitting the version from the manifest would be my ideal workflow!

@epage
Copy link
Contributor

epage commented May 19, 2022

I suspect this is a large enough semantic change that this would need a major change proposal or an RFC and need a person to champion the proposal and implementation. It is unlikely for someone else to pick up and do all of that leg work on behalf of someone interested in this work. One challenge though is cargo is currently on a soft feature freeze and the cargo team is wanting to avoid distractions, including mentoring / shepherding, to be able to handle basic maintenance, finish currently committed work ,and to reduce technical debt so we can increase our capacity.

Things off the top of my head that would need to be figured out

  • Should the version field be made completely optional? See feat(toml): Allow version-less manifests #12786
  • What are cargo's semantics without a version field and when no version is specified?
  • What all commands should accept a version?
  • How do we pass in versions, especially when running against multiple packages?
  • How do we help people when they miss specifying the version for one of the multiple packages?
  • How do we help workspace users with path dependencies?
    • Currently, to publish a crate with a path dependency, it also needs to have a version requirement specified. That version requirement must match the crate its pointing to. Tools like cargo-release handle updating version requirements of dependents so they don't become stale
  • Is there any prior art in other ecosystems that we can learn lessons from?
  • EDIT: How would patching a registry dependency with git work as we require the versions to match?
  • EDIT: What packages in the tree can have their versions overwritten (workspace member, local package, all packages)?

General concerns:

  • EDIT: Cargo having further direct integrations with specific VCSs as it either gives git a special status (git dependencies), the Cargo team is having to pick winners and losers and evolve them without breaking compatibility (cargo new --vcs, Add support for jujutsu VCS #12102, cargo new --svn flag #9729)

@burdiyan
Copy link

burdiyan commented May 19, 2022

@epage This is a nice summary to figure out a considerate solution to this problem! Thanks for that!

It's a pity that it's unlikely that this effort would come from the core team, but it's understandable.

On the other hand, maybe an easier and good enough solution could be adding a flag (or env var) to pass the desired version to cargo so it could ignore the one specified in Cargo.toml? I'm absolutely unaware of cargo's internals, but if there's only one place where Cargo.toml is read to extract the version, then it seems like a pretty easy change to make.

In my (somewhat biased) opinion: there should be no static file defining the "current" version of a package at all. This should be determined at release time by looking up the VCS info or elsewhere.

@weihanglo
Copy link
Member

A relevant discussion on Zulip. tl;dr: some build systems need a new version assigned for every build. It would be interesting if they could use build metadata, but there are reasons build metadata doesn't work.

  • crates.io doesn't allow two crate versions only differ in build metadata.
  • Build metadata, by Semver spec, shouldn't affect version precedence.

This may be similar to @sunhaitao's use case?

@sunhaitao
Copy link

Before I forget, I thought this post about Python's dynamic metadata interesting: https://lucumr.pocoo.org/2024/11/26/python-packaging-metadata/

For cases I just verified, pip wheel generates metadata by saving calculated dynamic values for both setup.py-based and pyproject.toml-based packages. So the problem may only exists on other distribution forms.

One difference between Python and Cargo is that Cargo rewrites the static metadata on publish, so we can take something dynamic and make it static, and we have an Index. However, that doesn't completely remove the complexities.

I think the good balance is: The final package should have static metadata. But they don't all have to be hard-coded in the source. Some of them should be able to be decided on publish. Allowing all of them being so dynamic seems unnecessary.

@sunhaitao
Copy link

@epage

Could you go into more detail on this?

Actually, we align the {MINOR} part to "How many days have passed since we started working on this {MAJOR} version". If we pause works on a package for a while, the {MINOR} part will jump several numbers on resuming.

And we don't publish all those versions to end users. Many of them only exist on developer machines. Some on build machines. Only few of them are released out.

@sunhaitao
Copy link

sunhaitao commented Dec 14, 2024

@weihanglo

It is similar on frequently assigning a version. But on build machines, we only dare to touch the {version core} part on each commit.

Anyway, changing the version a little on each build is a important use case for this feature.

@michaelboyles
Copy link

I'm just joining this discussion now. I thought better than responding to individual comments which are sometimes quite old, the best thing I could do was state my case mostly from scratch. Sorry if anyone feels like rehashes old ground.

My use-case

In the case of Rust, I'm an application author. I'm not publishing libraries. I want to be able to specify the version from some external source, and I don't want to edit any files. Certainly not manually, but ideally not even programmatically.

My usual workflow - one I've successfully used in other ecosystems - is that application versions are completely managed by git tags. In the source code and config files, you will never find a version (except possibly in a readme). When I create a release tag (i.e. vx.y.z format), CI will start a release build, set the version via CLI, and that version will end up wherever it needs to be: output filenames, as a constant in the binary, etc.

I don't want to have to manually edit a TOML file, commit it, and then tag that commit. In the past, I've worked for companies where QA liked to sign-off on a precise commit hash. If QA test a version and then the team later decides "let's release that as 1.0.0", then with Cargo I have to add more commits to specify the version, and the hash they signed-off on doesn't match any more.

Another problem is that if a version is specified by both a tag and a TOML file, then it's possible for them to be out of sync. What if my tag is 2.0.0 but my toml says 1.0.0? It's just clearer if there is one source of truth, and I've had good experiences using tags as the source of truth.

There's at least two workarounds I can think of:
- I could ignore cargo version altogether and just specify a custom environment variable. I think for my use-case this is enough, because the TOML is never used as an build artifact anyway. But this wouldn't work for a library.
- I could have my CI process edit the TOML file but not actually commit it, for example using cargo-edit. One reason I dislike this is because sometimes I have other tools that gather git metadata. They usually have something like an "isDirty" flag, which says whether there is some change in the local tree that's not checked in. It's a bit annoying to have to work around that by ensuring every change is appropriately reverted.

I would like some equivalent command to cargo build --config package.version="0.1.2" which specifies the version, rather than specify it in a file.

What Java does

I come from a Java background and Maven supports this, with what they call "CI friendly versions". It's explained here https://maven.apache.org/guides/mini/guide-maven-ci-friendly.html

You can see a personal example where I use this pattern here:

POM, somewhat equivalent to Cargo.toml: https://github.com/michaelboyles/s3-maven-extension/blob/develop/pom.xml#L9C5-L9C35

GitHub build: https://github.com/michaelboyles/s3-maven-extension/blob/develop/.github/workflows/build.yml#L38

Maven has multi-module support, where each module may theoretically have a unique version, but they don't provide any way to specify those versions independently via CLI: you only get one CLI-settable "revision". I've never seen anyone complain that that wasn't sufficient. So maybe the answer to "How do we pass in versions, especially when running against multiple packages?" is "you don't, and that's fine".

Since Maven's POM is itself an uploadable artifact, there's an additional step required to erase the placeholders to produce a final artifact POM that's suitable for consumers (e.g. replace ${revision} with 1.0.0). That's usually done with this plugin. I've never published a Rust crate, but I've inferred from the existence of both Cargo.toml and Cargo.toml.orig that some kind of "flattening" (in Maven parlance) is already happening. So it doesn't seem a problem that the source Cargo.toml may not specify a version: the flattened one would, and the flattening process already exists.

@faern
Copy link
Contributor

faern commented Feb 13, 2025

Why do you need cargo to know about your version at all? You want to manage the version outside cargo (Cargo.toml) so why do you need cargo to know about the version at all (cargo build --blabla)? Why is it important to you that cargo knows about your applications version? This is not clear to me.

I posted a solution further up that does what you want and works well for applications. It's even cleaner now than when I posted it, since now the entire version field is optional in Cargo.toml. If your application is a single crate, you don't even need a separate crate for the version computation, it can be inside your main crate.: #6583 (comment)

@michaelboyles
Copy link

@faern assuming that was a reply to me

I didn't say I need it. I ended up here after fiddling unsuccessfully with --config for a while. I was expecting there to be a way to do it, since there is in similar tools. Its absence made for a clumsy experience for me. I can't say whether that's a representative experience or not

I don't currently do this with Rust, but with Maven I publish both libraries and apps into Sonatype Nexus. There's no real reason the apps have to be stored there, because Maven is never going to download them, but it's a convenient structured storage that prevents overwriting a release version, and we already need it for libraries anyway. I could foresee wanting to cargo publish my apps there too, and it wouldn't be possible unless Cargo knows the correct version

This would give application authors a convenient, consistent and idiomatic way to specify versions, rather than having to invent something for themselves. That seems desirable whether or not it's a necessity

@blefaudeux
Copy link

Why do you need cargo to know about your version at all?

for instance because with maturin your wheel will end up with the version number that cargo gives it ? Specific example I know, but I landed in this thread with the exact same need as many others it seems. Editing the cargo.toml (even programmatically) makes little practical sense, while versioning artifacts is recognized good practice ?

I posted a solution further up that does what you want and works well for applications. It's even cleaner now than when I posted it, since now the entire version field is optional in Cargo.toml.

Thanks for posting it, it's clever, but still feels like fighting against the tooling here, not exactly straightforward ?

@DavidAntliff
Copy link

DavidAntliff commented Mar 2, 2025

There's a bit of buck-passing going on here. The maturin project says they'll only use the cargo version, and won't provide an override, so we must use the cargo version to set the package version, yet the cargo project says that you should use a separate version number, not the cargo version number. The two approaches aren't compatible.

@blefaudeux
Copy link

There's a bit of buck-passing going on here. The maturin project says they'll only use the cargo version, and won't provide an override, so we must use the cargo version to set the package version, yet the cargo project says that you should use a separate version number, not the cargo version number. The two approaches aren't compatible.

In my case it works fine eventually, I was mentioning this as an example really. There are a bunch of examples in this thread showing that while the file based approach makes sense, modern CD is easier to set up when version can be flag based. That's pretty much it, CD pipelines are not file based with respect to version, typically. I think the collision is here more than with Maturin (which just delegates this upstream).

@eirnym
Copy link

eirnym commented Mar 30, 2025

@epage You proposed plugin API before. It would definitely help to ease the situation, introducing algorithms which could be tested faster and easier outside core code.

Currently bumping version and release a new one can't be done in a single workflow with no human interaction.

Current workflow is following:

  • create a PR with version bumped
  • Even if this PR could be created by automation tools, cargo-semver-checks or similar tools are required to run to verify the process (even these tools were used on the step before)
  • Run all CI things project needs to run
  • Review this PR to make sure no other changes have been introduced
  • Merge PR
  • Run actual release action

At the last step we can't be sure that between first and last steps there were no commits/merges

This workflow

  • Is too complicated with too many moving parts => too long to be done
  • Requires human attention as may be fail in the middle
  • Makes impossible to publish code at given hash to production
  • Is automated with other tools and languages using native tools

@eirnym
Copy link

eirnym commented Mar 31, 2025

Let's convert my previous comment to company costs. As shown below they at least 2-5 times higher per each release, thus cheaper alternatives like "write on another language" or "fork and maintain fork of cargo" are way cheaper than current situation.

In a medium plus size company normal releases of apps happen usually once in 2-4 weeks per app/library.

This cost has also highly negative impact on critical issues on production, when developer may need to fix an issue at at night.

Times on localhost for comparison for a small library I've wrote recently. Everyone can scale them to CI and their size of app/test coverage

  • UT/integration tests (with 100% coverage): ~2-5 seconds
  • Mutagen run: ~40 seconds

when build system accepts CI version:

  • Tester team tests library/app
  • -> release rejection might happen here
  • Developer runs release process
  • -> UT/integration tests run
  • -> release build for various platforms
  • -> publication process
  • Deployment review
  • -> release most likely is not yanked
  • Deployment process

when build system doesn't accepts CI version:

  • Tester team tests library/app
  • -> release rejection might happen here
  • Developer starts merge/commit-freeze
  • Developer runs create version process which creates PR (no direct pushes are allowed to main branch)
  • -> UT/integration tests run
  • -> mutagen and other verify tools are run
  • Developer reviews the PR and merges it
  • Developer runs release process
  • -> UT/integration tests run
  • -> release build for various platforms
  • -> publication process
  • Deployment is done
  • Tester team tests library/app
  • -> release rejection might happen here
  • Deployment review
  • -> release most likely is not yanked
  • Deployment process

Additional run of tests including time of tester team is costly for a company and it's easier to reject Rust as a language of choice or override the issue by writing and maintaining their release tool instead of using cargo itself.

@boozook
Copy link

boozook commented Mar 31, 2025

I'm pretty sure there should be no more than one "source of knowledge" for the crate, such as version. Imho, this is a wrong direction and will entail serious problems for cargo and the entire ecosystem.

Next step is VERSION.TXT and cargo publish --version=$(cat VERSION.TXT).

@eirnym
Copy link

eirnym commented Mar 31, 2025

I'm pretty sure there should be no more than one "source of knowledge" for the crate, such as version. Imho, this is a wrong direction and will entail serious problems for cargo and the entire ecosystem.

Next step is VERSION.TXT and cargo publish --version=$(cat VERSION.TXT).

if cargo would accept such parameter, it would be enough to fix the issue. As shown above Cargo.toml in repository is not always the best place to manage the version.

@epage
Copy link
Contributor

epage commented Mar 31, 2025

@eirnym

In a medium plus size company normal releases of apps happen usually once in 2-4 weeks per app/library.

Are you using the version for lookups in an internal or public registry or for the marketing version? If for the marketing version, could you explain why you want to use package.version for that?

@eirnym
Copy link

eirnym commented Mar 31, 2025

Private registry is considered as primary target and public registry for open source parts.

My company rejected proposition to rewrite part of apps and libs to rust due to fact, that cargo can't be aligned to the process. Forking cargo for packaging was considered, but rejected as company wasn't sure if they want to invest in Rust.

Some of my personal projects aren't released to the public registry because of that exact reason as well. Searching for a acceptable solution with automation took me too long. Probably, I'll release them without automation, even this not how I prefer.

@kyle-rader-msft
Copy link

kyle-rader-msft commented Apr 3, 2025

Shout out to @psFried 's build.rs solution posted in the middle of the discussion above: #6583 (comment)

I am building CLIs in AzureDevops and we use Date based versions, and this is working to at least get the version reported by the CLI itself to what we want.

Update: PSA from @eirnym that this doesn't influence cargo publish - but at least for me, I'm shipping pre-built binary tools via nuget packages, so we can easily pass this version to nuget.

// build.rs
fn main() {
    // Set the version to the build ID if it exists
    if let Ok(version) = std::env::var("BUILD_BUILDID") {
        println!("cargo:rustc-env=CARGO_PKG_VERSION={}", version);
    }
    println!("cargo:rerun-if-env-changed=BUILD_BUILDID");
}

@eirnym
Copy link

eirnym commented Apr 4, 2025

@kyle-rader-msft works well for marketing version, but it won't fool cargo publish :(

This article describes how to extend build.rs to support export-subst and git describe commands. obviously, these scripts can be extended further to make them more robust.

@eirnym
Copy link

eirnym commented Apr 4, 2025

I've gave a thought on what possible options do we have and wrote following proposition. Please, comment and propose your solutions

Possible solutions

Here I describe various possible solutions, which maybe combined together.

All solutions must utilize semver to verify input and reject the rest.

In following document I only respect Git SCM, as I don't take solution "which works everywhere with everything from day zero" as a good practice.

Environment variable

Environment variable to set the version.

Advantages of this solution:

  • Solution is scalable for any other possible extensions
  • commonly understood
  • easy to set up
  • no need to worry about SCM, prefixes and suffixes

Disadvantages of this solution:

  • Repository which has been downloaded as git archive may not have reference if there's no build script included
  • Repository which has been downloaded as git checkout may not have reference if there's no build script included

Command line parameter

Similar advantages and advantages to an environment variable solution, but it isn't scalable.

build.rs

Using existing build.rs infrastructure. This will require Cargo itself to read and follow the output for changes to reflect them in resulting Cargo.toml file and registries.

e.g. Cargo reads Cargo.toml, sets up variables, run build.rs, output will be read by Cargo as well to adjust some parameters. (This must be limited using allow list)

Advantages of this solution:

  • Solution is scalable for any other possible extensions
  • Cover prefixes
  • Repository which has been downloaded as git archive may be covered using export-subst git attribute
  • Repository which has been downloaded as git checkout may be covered by git describe --tags
  • build.rs has support of external dependencies using build-dependencies.
  • Implementation may take leverage from setuptools-scm and other versioning libs for other build systems.

NOTE: git tags may be fetched without fetching whole repository as shown in https://github.com/actions/checkout.

Disadvantages of this solution:

  • Harder to implement correctly
  • Harder to support (need to be copied everywhere)
  • Versioning will fail if tags have not been fetched.

Additional cargo.rs

Very similar to build.rs, but strictly for altering few parts in Cargo.toml

Disadvantages of this solution:

  • One more moving part to support.

Scm version in Cargo.toml, e.g. version="scm"

After good and stable implementation in build.rs or similar script is emerged, cargo may implement this solution for given SCM. Not earlier.

@eirnym
Copy link

eirnym commented Apr 4, 2025

My personal opinion on what I've wrote above (cut out this part, as it is not main part of implementation proposition)

  • I vote for Environment variable and build.rs solutions to come along
  • I vote strongly against attitude "we need to support every possible SCM from day 0"
  • I vote strongly against cargo.rs as we already have build.rs
  • I vote strongly against version="git" to support git only
  • I vote against version="scm" before we have a library, which will be used to determine such version.

@ByteB4rb1e
Copy link

ByteB4rb1e commented Apr 7, 2025

Scm version in Cargo.toml, e.g. version="scm"

After good and stable implementation in build.rs or similar script is emerged, cargo may implement this solution for given SCM. Not earlier.

Hmm... Trying to come up with an interims solution myself that is satisfying to me makes my brain hurt... Mainly because I'm so used to the conventions of build tools of other languages and am trying to apply the same way of thinking to cargo, which I think is a fallacy. I could at least see the conventional Apache Maven, Python setuptools-scm, and versioneer approach throughout this discussion.

Maybe it would be an option to just define a very simple interface for an external mechanism and just require that mechanism to provide any valid semver string? The external mechanism could be identified by a simple pattern that does not collide with the semver pattern, like prefixing with extern: to keep it aligned with other Rust crate conventions. Then cargo just needs to be able to call whatever is defined. This could then also be extended in the sense of scheming, like builtin:git, or so.

Assuming a binary, or a file with a shebang (therefore executing through the shell environment), something like:

version="extern:./scripts/version.sh"

Assuming a command, something like:

version="extern:awesome-versioning-tool ./"

In the end, all I want is for my Git working tree to stay clean, after I've applied a tag. If versioning was handled completely outside of cargo, I don't see a reason as to try and implement an interface for every use-case that might come up. That's the job of whoever defines the external mechanism, in my opinion.

@WhyNotHugo
Copy link

@eirnym: You mention an environment variable. Read by which process? Set by which one? Do you envision one that can be set in build.rs?

@eirnym
Copy link

eirnym commented Apr 8, 2025

@WhyNotHugo, @ByteB4rb1e

Currently only marketing version can be set by build.rs. My proposal for environment variable is to read CARGO_PKG_VERSION or something similar by cargo itself and then read part of output build.rs to adjust metadata.

Environment will remove requirement to always have build.rs file if only version set by CI is needed and where external scripts provide it. Then version is checked if it conforms standards.

Why library first? Because if we'd have a library for build.rs with good enough implementation (such as mentioned setuptools-scm or … one of many others), discussion how to implement it in cargo would be more practical, than theoretical (as cargo might use such library directly or use it as a plugin of some sort), this would also highlight many corner cases versioning might have.

Due to git popularity, limiting to it first also might give better overview of corner cases and list of possible settings and user preferences.

@ByteB4rb1e
Copy link

Why library first? Because if we'd have a library for build.rs with good enough implementation (such as mentioned setuptools-scm or … one of many others), discussion how to implement it in cargo would be more practical, than theoretical (as cargo might use such library directly or use it as a plugin of some sort), this would also highlight many corner cases versioning might have.

@eirnym, I see what you mean. I think I conflated two separate issues, pardon... I was describing a separate use-case: to set the version dynamically and forward it to mechanisms like cargo release. The nature of the discussion though is about utilizing the version somewhere in the build process itself. Those need different considerations. So I'd agree with your original assessment and the discussion on how to adapt the cargo PackageIdSpec to cater to multiple use-cases should be handled later on.

@eirnym
Copy link

eirnym commented Apr 8, 2025

I see misunderstanding as well.

"Markeing version", as has been used by @epage is set the version for a binary which is never published. This version can be set at this moment by passing variable to rustc via build.rs

I'm concentrating and commenting exclusively on publishing version, which is propagated in resulting Cargo.toml and package version. Library used for build.rs for that goal is possible only if Cargo would read some variables back after running build.rs to adjust crate metadata.

@epage
Copy link
Contributor

epage commented Apr 9, 2025

@eirnym thank you for taking the time to enumerate the different options!

Could you update your solution alternatives to address the considerations from

As for build.rs / cargo.rs, I could see there being a cultural hesitation around imperative project metadata as I've seen people express bad experiences with it from other systems. If someone were to propose something in those directions, they should probably dig into those problems and weigh them appropriately. A concern I have specifically with build.rs is that we won't have the version number until very late in the process and it could require building a lot just to determine the version number. A cargo.rs could reduce that somewhat by being more purpose driven and having a specialized dependency table. A concern I've seen from others that would need to be overcome with a specialized dependency table is the ecosystem churn from adding one. In general, we are wanting to work towards reducing the need for build scripts (#14948). However, #14903, specifically my proposal at #14903 (comment), might offer another interesting design direction to take dynamic versions.

@eirnym
Copy link

eirnym commented Apr 9, 2025

Thank you @epage, I'll take a look and answer all questions as soon as possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-manifest Area: Cargo.toml issues C-feature-request Category: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted` S-needs-rfc Status: Needs an RFC to make progress.
Projects
None yet
Development

No branches or pull requests