Renaming Cargo "target"

Cargo currently uses the word "target" for multiple different things:

  1. The platform target: x86_64_unknown-linux-gnu, aarch64-unknown-linux-musl, etc. This usage appears in rustc in names like target_os/target_cpu, and in Cargo in the [target.*.dependencies] tables.
  2. Things like "bin" and "lib" and "examples" and "tests", referenced in Cargo Targets - The Cargo Book and by the --all-targets option.

This ambiguity regularly creates confusion, and such awe-inspiring sentences as "which targets work on this target?".

In today's Cargo team meeting, we talked about ways we could resolve this ambiguity and confusion. We agreed that usage (1) is more pervasive, because of the established usage in both rustc and cargo. Given that, we tried to come up with a better name for (2). Our leading candidate was "artifact"; we considered various other possibilities but couldn't come up with better options that were both self-explanatory and unambiguous.

As it turns out, we also have another reason to replace the --all-targets option: it doesn't actually include everything, because it doesn't include doctests. So, rather than introducing an --all-targets-including-doctests option or similar, we can introduce --all-artifacts and deprecate --all-targets in favor of that.

We're going to file an issue and FCP it to formally make the decision, but before doing so, we wanted to bring this up on internals to see what people thought.

25 Likes

The target directory is probably derived from meaning (2) rather than meaning (1), if not a third thing altogether. I assume it would break too many things to change either the default value or the config option name, though.

2 Likes

I think an important question to ask here is whether this matches up with the use of the word in artifact dependencies. On a quick skim, it seems that it almost matches, but has an important difference: the syntax in the artifact dependencies RFC uses “artifact” to refer to a single file — a single Cargo target might produce multiple artifacts of this sort when crate-type is set to a list of more than one element.

I'm not familiar enough with the use of “artifact” in other build systems to say which of these two meanings should get to use the word.

6 Likes

This came up in the discussion, and we had the same position of "it almost matches". We felt like it was close enough to not be confusing, particularly since (for instance) you can talk about one bin artifact and not just all bin artifacts.

1 Like

the target/ directory is a bit annoying due it sometimes having use 1, but always then having use 2.

Near certainly not worth changing it though, maybe other than making it configurable.

2 Likes

Yes, target-dir is referring to the place to store build targets but we also have plans for changing this where target-dir would slowly be phased out though we can't change the default location of target/ for what replaces it.

The idea is to split target-dir into two meanings

  • build or working directory for all intermediate artifacts
  • artifacts dir for final build artifacts

So except for target/ being used for compatibility reasons, the change proposed in this thread would further improve consistency.

See Redefine `CARGO_TARGET_DIR` to be only an artifacts directory · Issue #14125 · rust-lang/cargo · GitHub

2 Likes

I talked about artifact deps further on zulip. I wonder if this is an opportunity to re-evaluate it. Artifact deps is an odd way of mixing build-target with crate-type with a separate field exclusively for one build-type (lib).

The main concerns we had in the Cargo team when we discussed this were

  • An "artifact" can actually have multiple artifacts, one per crate-type.
    • Not as many users are likely to come in contact with this level of ambiguity as the current "target" ambiguity
  • Not all "artifacts" have artifacts (ie copied out of intermediate locations into final locations, e.g. tests)
    • Likely close enough for now and we can always evolve things to allow more artifacts to be produced
    • You could twist things around to say that the test results are the artifact.

Other things we considered

  • cargo check --all-artifacts: this isn't saying to produce artifacts from cargo check but that we are only checking, and not building, the artifacts
  • Our use of "build target" is derived from make and the proper terms there are "target" (binary), "recipe" (table in manifest), "dependency" (source). We've come to refer to all of these as "target" but really they are "artifact", "artifact definition or spec", and "artifact source".
2 Likes

I wasn't aware that this was a big issue (or issue at all), so I'd just like to ask for clarification: for whom does it create confusion? New users? Team members talking about working on how targets are handling and not being sure of which meaning the other person is referring to?

Both could be valid reasons to change it, but it would be good to have that perspective.

One followup question (especially if it is the former): what do other language ecosystems call the equivalent thing? I'm most familar with C++ outside of Rust, but I don't think I have heard much of an explicit name (binaries, libraries, build rules, output etc but while adjecent they are not the same thing).

1 Like

I really dislike the choice of "artifact" for this, since it already has a meaning in the context of compiled languages as the output of a compilation process.

That's exactly the meaning being invoked here.

Users (both new and experienced) and team members, both.

Many of us on the Cargo team, in addition to encountering this ambiguity personally and in team discussions, have seen instances of this in many projects that need to refer to "targets".

Consider a sentence like "this dependency should only apply to particular targets". Does that mean "this dependency should only apply on Windows / on macOS / on Linux / etc"? Or does it mean "this dependency should only apply when building the binary, but not when building the library"?

3 Likes

But that's the opposite of the meaning of cargo-targets. Cargo targets are inputs, artifacts are outputs.

I think a good example of where "target" ambiguity can make things less clear is the following command

$ cargo check --all-targets --target x86_64-pc-windows-msvc

"target" is being used with two different meanings and there is no context, outside of prior knowledge, what they mean.


From our research notes on other ecosystems:

  • Ant
  • Buck/Buck2
    • "target"
  • Bazel
  • CMake
  • Gradle
  • make
    • "target"
  • MSBuild
    • A named "Target" performs a set of Tasks to build something.
  • ninja
    • A "target" is the output file that is being generated.
  • Maven
  • Meson
  • Rake
  • Scons
  • go
  • NuGet
    • n/a, only really handles dependencies between packages, see MSBuild.
  • Cabal (Haskell)
    • "Component" is a library/binary/test-suit/foreign-library/benchmark
  • Pkg (Julia)
    • Doesn't really have a term. It has two things, Package (a library) and Application (executable).
  • RubyGems
    • gemspec file describes a gem. It has a list of files for the gem.
  • Rake
    • "task" describes prerequisites and actions.
    • Can be built from "rules".
  • Bundler
    • n/a, I think it just handles dependencies, see - Rake
  • pip
  • CPAN
  • npm
  • Yarn
    • not really applicable. modules, scripts, etc.
  • Parcel
  • Grunt
    • Tasks
  • Gulp
  • sbt
    • Each thing is a "subproject" (or "Project").
  • pub
    • executables, scripts, libraries, entrypoints
  • Dune
  • opam
  • Dub
  • Elm
  • LuaRocks
6 Likes

Cargo targets are outputs. The name is lifted from Make where a target is the output of a recipe. However, we've been loose with the terminology and have applied "target" to the final build units source, the final build units definition, and the final build units output as the source and definition are stand-ins for the output. See also Renaming Cargo "target" - #8 by epage

1 Like

Your list suggests that target is common for this. And C/C++ definitely have target in the other meaning ("I'm cross compiling from x86-64 to my target", "target triplet", "target for the cross compiler", etc).

Though there that term perhaps come up in the context of higher level build systems (autoconf, cmake, etc) and the other meaning is more common when talking about the build systems they generate (make, NINJA, ...). Not sure where bazel/buck/buck2 fits there as they (just like cargo) are "do everything" tools.

I have not heard about this being a point of confusion in the C++ world (but the again I hadn't been confused by this in Rust either, so take that with a grain of salt).

That said, artifacts tend to mean the final artifacts in my experience (e.g. whatever the CI pipeline decides to upload at the very end). If cargo is your only build system (as might be the case for simple console programs that don't need to distribute more than a single binary) that would make sense. But even most console programs might need to include shell completion files and as such the actual artifact tend to be composites of cargo targets and other files.

But I don't feel strongly either way about the question between target / artifact. I just think that the "alternatives" and "prior art" sections of the RFC will need to fleshed out a fair bit.

For this, I'd need to see representative cases of how "target" being used for those two meanings in close proximity / in the same abstraction. Looking at cmake, I don't see much. Looking at buck2 build, I think they mix the terms (but not flags); its hard to tell because they leave out enough general context to understand it, so I guess that illustrates the problem? I'm not finding an equivalent of that cargo check command I mentioned earlier.

That said, I don't think there is a good equivalent of Cargo that has high "market penetration" for C++. I expect make/cmake to be high up but those are low level. I doubt there is much use of buck2/bazel and even those are lower level than Cargo. Also, C++ build systems aren't always the best benchmark for usability.

1 Like

Very true. My concern is mostly "is there a risk we confuse people coming from other ecosystems when we invent new terms for things".

Even as something as simple as "crate" I need to suffix with "that's like a static library or package" when talking to people who are new to Rust (I mostly talk with people who have a C++ and/or Python background in my dayjob, so those are the relevant terms there). Obviously that particular ship has long since sailed, but it is good to not add additional unnecessary terminology differences.

1 Like

I feel like "artifact" is a great solution given the constraints as stated:

That said, if usage of "target" outside of Cargo was less pervasive (and up for discussion), then "platform" seems like a better fit for (1). Mostly thinking in terms of nix cross compilation. The rustc platform support page would read more naturally if platforms weren't called targets.

If we've agreed that ship has already sailed, then this is kind of off topic, but wanted to share my thoughts.

3 Likes

Though I think this change is a good idea, one more data point: SwiftPM uses “target” in the “artifact” sense (inherited from Xcode). Its cross-compilation support isn’t quite as simple as Cargo’s, though, so they got to stuff that sense of “target” behind “SDK” and “toolchain” and “platform/OS” (and apparently “triple”, sigh). swiftc, the compiler, uses “target” in the “platform+architecture” sense.