Target-dependent default features in Cargo


#1

I’m building a “-sys” package that can either link dynamically or statically. I’d like user of the package to be able to choose which way they want, so I have “dynamic” and “static” [features] in Cargo.toml

Dynamic linking is a good default for unix machines, but on Windows static has to be the default. The problem is that Cargo doesn’t seem to allow platform-dependent features:

[target.'cfg(windows)'.features]
default = ["static"]

warning: unused manifest key: target.cfg(windows).features

Is there a way to have a different default for Windows? I’d rather avoid having a package that’s unusable on Windows unless someone adds a feature flag manually.


#2

Do not use cargo features for choosing how to link something. Instead you should let the user choose how to link it via an environment variable which the build script for your crate will read and obey.


#3

What are the problems with using features for this?

I went for features, because:

  • they’re standard and discoverable within Cargo,
  • can be set in Cargo.toml of the parent crate,
  • I can avoid unused build dependencies
  • clang-sys does it.

#4

The problem with cargo features is they’re meant to do exactly one thing, and that is to represent additive API additions only. As soon as you try to use them to represent things which are mutually exclusive, things tend to fall apart. The way cargo features work, two downstream crates can request different features from your crate and cargo will build your crate once with all the features that were requested. If they requested mutually exclusive features, well, now what?

can be set in Cargo.toml of the parent crate

Why would the parent crate care about whether a library is being linked to statically or dynamically? The only entity who should care is the user building their application. Maybe clang-sys is some deep transitive dependency. How is the user going to specify how they want clang linked?

Also to answer your original question, no, you cannot have a different set of default features for each target. Also, I really hate default features because if I have a transitive dependency with some default features and I don’t want those default features, unless the crate which pulled in that transitive dependency remembered to disable that default feature, I am powerless to turn off those default features.

I really hope someone writes up a proposal for a better way to let the user configure aspects of a given, possibly transitive, dependency.


#5

Sorry for the necro, but another reason to forego cargo features in this situation of mutually exclusive code paths is that when one packages / publishes their crate – there is no --features option. Thus, to stick with cargo features, you’d need to specify a default feature, something that doesn’t always make sense.