Git Product home page Git Product logo

cargo-raze's Introduction

cargo-raze: Bazel BUILD generation for Rust Crates

cargo-raze is currently an unmaintained and unsupported product.

crate_universe in rules_rust is a supported and maintained product with similar aims.

We believe crate_universe supports all of the functionality which was supported by cargo-raze. We encourage you to try to migrate to it. If you find missing support, or bugs, please file an issue or PR against rules_rust.

cargo-raze has been archived to reflect the reality that PRs and Issues filed against it are unlikely to be addressed.

Please reach out to #rust in the Bazel Slack if you are interested in taking over maintenance of this project.

Build status

An experimental support Cargo plugin for distilling a workspace-level Cargo.toml into BUILD targets that code using rules_rust can depend on directly.

Disclaimer

This is not an official Google product (experimental or otherwise), it is just code that happens to be owned by Google.

Overview

This project synthesizes the dependency resolution logic and some of the functionality of Cargo such as features and build scripts into executable rules that Bazel can run to compile Rust crates. Though the standard rules_rust rules can be used to compile Rust code from scratch, the fine granularity of the dependency ecosystem makes transforming dependency trees based on that ecosystem onerous, even for code with few dependencies.

Usage

cargo-raze can generate buildable targets in one of two modes: Vendoring, or Non-Vendoring. In the vendoring mode, developers use the common cargo vendor subcommand to retrieve the dependencies indicated by their workspace Cargo.toml into directories that cargo-raze then populates with BUILD files. In the non-vendoring mode, cargo-raze generates a flat list of BUILD files, and a workspace-level macro that can be invoked in the WORKSPACE file to pull down the dependencies automatically in similar fashion to Cargo itself.

In both cases, the first step is to decide where to situate the Cargo dependencies in the workspace. This library was designed with monorepos in mind, where an organization decides upon a set of dependencies that everyone points at. It is intended that stakeholders in the dependencies collaborate to upgrade dependencies atomically, and fix breakages across their codebase simultaneously. In the event that this isn't feasible, it is still possible to use cargo-raze in a decentralized scenario, but it's unlikely that such decoupled repositories would interact well together with the current implementation.

Regardless of the approach chosen, the rust_rules should be brought in to the WORKSPACE. Here is an example:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "rules_rust",
    sha256 = "accb5a89cbe63d55dcdae85938e56ff3aa56f21eb847ed826a28a83db8500ae6",
    strip_prefix = "rules_rust-9aa49569b2b0dacecc51c05cee52708b7255bd98",
    urls = [
        # Main branch as of 2021-02-19
        "https://github.com/bazelbuild/rules_rust/archive/9aa49569b2b0dacecc51c05cee52708b7255bd98.tar.gz",
    ],
)

load("@rules_rust//rust:repositories.bzl", "rust_repositories")

rust_repositories(edition="2018")

Generate a Cargo.toml

For Bazel only projects, users should first generate a standard Cargo.toml with the dependencies of interest. Take care to include a [lib] directive so that Cargo does not complain about missing source files for this mock crate. Here is an example:

[package]
name = "compile_with_bazel"
version = "0.0.0"

# Mandatory (or Cargo tooling is unhappy)
[lib]
path = "fake_lib.rs"

[dependencies]
log = "=0.3.6"

Once the standard Cargo.toml is in place, add the [package.metadata.raze] directives per the next section.

Using existing Cargo.toml

Almost all canonical cargo setups should be able to function inplace with cargo-raze. Assuming the Cargo workspace is now nested under a Bazel workspace, Users can simply add RazeSettings to their Cargo.toml files to be used for generating Bazel files

# Above this line should be the contents of your Cargo.toml file

[package.metadata.raze]
# The path at which to write output files.
#
# `cargo raze` will generate Bazel-compatible BUILD files into this path.
# This can either be a relative path (e.g. "foo/bar"), relative to this
# Cargo.toml file; or relative to the Bazel workspace root (e.g. "//foo/bar").
workspace_path = "//cargo"

# This causes aliases for dependencies to be rendered in the BUILD
# file located next to this `Cargo.toml` file.
package_aliases_dir = "."

# The set of targets to generate BUILD rules for.
targets = [
    "x86_64-apple-darwin",
    "x86_64-pc-windows-msvc",
    "x86_64-unknown-linux-gnu",
]

# The two acceptable options are "Remote" and "Vendored" which
# is used to indicate whether the user is using a non-vendored or
# vendored set of dependencies.
genmode = "Remote"

Cargo workspace projects

In projects that use cargo workspaces users should organize all of their raze settings into the [workspace.metadata.raze] field in the top level Cargo.toml file which contains the [workspace] definition. These settings should be identical to the ones seen in [package.metadata.raze] in the previous section. However, crate settings may still be placed in the Cargo.toml files of the workspace members:

# Above this line should be the contents of your package's Cargo.toml file

# Note that `some-dependency` is the name of an example dependency and
# `<0.3.0` is a semver version for the dependency crate's version. This
# should always be compaitble in some way with the dependency version
# specified in the `[dependencies]` section of the package defined in
# this file
[package.metadata.raze.crates.some-dependency.'<0.3.0']
additional_flags = [
    "--cfg=optional_feature_a",
    "--cfg=optional_feature_b",
]

# This demonstrates that multiple crate settings may be defined.
[package.metadata.raze.crates.some-other-dependency.'*']
additional_flags = [
    "--cfg=special_feature",
]

Remote Dependency Mode

In Remote mode, a directory similar to the vendoring mode is selected. In this case, though, it contains only BUILD files, a vendoring instruction for the WORKSPACE, and aliases to the explicit dependencies. Slightly different plumbing is required.

This tells Raze not to expect the dependencies to be vendored and to generate different files.

Generate buildable targets

First, install cargo-raze.

$ cargo install cargo-raze

Next, execute cargo raze from within the cargo directory

$ cargo raze

Finally, invoke the remote library fetching function within your WORKSPACE:

load("//cargo:crates.bzl", "raze_fetch_remote_crates")

# Note that this method's name depends on your gen_workspace_prefix setting.
# `raze` is the default prefix.
raze_fetch_remote_crates()

This tells Bazel where to get the dependencies from, and how to build them: using the files generated into //cargo.

You can depend on any explicit dependencies in any Rust rule by depending on //cargo:your_dependency_name.

Vendoring Mode

In Vendoring mode, a root directly is selected that will house the vendored dependencies and become the gateway to those build rules. //cargo is conventional, but //third_party/cargo may be desirable to satisfy organizational needs. Vendoring directly into root isn't well supported due to implementation-specific idiosyncracies, but it may be supported in the future. From here forward, //cargo will be the assumed directory.

Generate buildable targets (vendored)

First, install the required tools for vendoring and generating BUILDable targets.

$ cargo install cargo-raze

Following that, vendor your dependencies from within the cargo/ directory. This will also update your Cargo.lock file.

$ cargo vendor --versioned-dirs

Finally, generate your BUILD files, again from within the cargo/ directory

$ cargo raze

You can now depend on any explicit dependencies in any Rust rule by depending on //cargo:your_dependency_name.

Using cargo-raze through Bazel

Cargo-raze can be built entirely in Bazel and used without needing to setup cargo on the host machine. To do so, simply add the following to the WORKSPACE file in your project:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "cargo_raze",
    sha256 = "c664e258ea79e7e4ec2f2b57bca8b1c37f11c8d5748e02b8224810da969eb681",
    strip_prefix = "cargo-raze-0.11.0",
    url = "https://github.com/google/cargo-raze/archive/v0.11.0.tar.gz",
)

load("@cargo_raze//:repositories.bzl", "cargo_raze_repositories")

cargo_raze_repositories()

load("@cargo_raze//:transitive_deps.bzl", "cargo_raze_transitive_deps")

cargo_raze_transitive_deps()

With this in place, users can run the @cargo_raze//:raze target to generate new BUILD files. eg:

bazel run @cargo_raze//:raze -- --manifest-path=$(realpath /Cargo.toml)

Note that users using the vendored genmode will still have to vendor their dependencies somehow as cargo-raze does not currently do this for you.

Handling Unconventional Crates

Some crates execute a "build script", which, while technically unrestricted in what it can do, usually does one of a few common things.

All options noted below are enumerated in the src/settings.rs file.

Crates that generate files using locally known information

In some cases, a crate uses only basic information in order to generate a Rust source file. These build-scripts rules can actually be executed and used within Bazel by including a directive in your Cargo.toml prior to generation:

[package.metadata.raze.crates.clang-sys.'0.21.1']
gen_buildrs = true

This setting tells cargo-raze to generate a rust_binary target for the build script and to direct its generated (OUT_DIR-style) outputs to the parent crate.

Crates that depend on certain flags being determined by a build script

Some build scripts conditionally emit directives to stdout that Cargo knows how to propagate. Unfortunately, its not so simple to manage build-time generated dependency information, so if the flags are statically known (perhaps, since the compilation target is statically known), they can be provided from within the Cargo.toml, in the following manner

[package.metadata.raze.crates.unicase.'2.1.0']
additional_flags = [
    # Rustc is 1.15, enable all optional settings
    "--cfg=__unicase__iter_cmp",
    "--cfg=__unicase__defauler_hasher",
]

Flags provided in this manner are directly handed to rustc. It may be helpful to refer to the build-script section of the documentation to interpret build scripts and stdout directives that are encountered, available here: https://doc.rust-lang.org/cargo/reference/build-scripts.html

Crates that need system libraries

There are two ways to provide system libraries that a crate needs for compilation. The first is to vendor the system library directly, craft a BUILD rule for it, and add the dependency to the corresponding -sys crate. For openssl, this may in part look like:

[package.metadata.raze.crates.openssl-sys.'0.9.24']
additional_flags = [
    # Vendored openssl is 1.0.2m
    "--cfg=ossl102",
    "--cfg=version=102",
]
additional_deps = [
    "@//third_party/openssl:crypto",
    "@//third_party/openssl:ssl",
]

[package.metadata.raze.crates.openssl.'0.10.2']
additional_flags = [
    # Vendored openssl is 1.0.2m
    "--cfg=ossl102",
    "--cfg=version=102",
    "--cfg=ossl10x",
]

In some cases, directly wiring up a local system dependency may be preferable. To do this, refer to the new_local_repository section of the Bazel documentation. For a precompiled version of llvm in a WORKSPACE, this may look something like:

new_local_repository(
    name = "llvm",
    build_file = "BUILD.llvm.bazel",
    path = "/usr/lib/llvm-3.9",
)

In a few cases, the sys crate may need to be overridden entirely. This can be facilitated by removing and supplementing dependencies in the Cargo.toml, pre-generation:

[package.metadata.raze.crates.sdl2.'0.31.0']
skipped_deps = [
    "sdl2-sys-0.31.0"
]
additional_deps = [
    "@//cargo/overrides/sdl2-sys:sdl2_sys"
]

Crates that supply useful binaries

Some crates provide useful binaries that themselves can be used as part of a compilation process: Bindgen is a great example. Bindgen produces Rust source files by processing C or C++ files. A directive can be added to the Cargo.toml to tell Bazel to expose such binaries for you:

[package.metadata.raze.crates.bindgen.'0.32.2']
gen_buildrs = true # needed to build bindgen
extra_aliased_targets = [
    "cargo_bin_bindgen"
]

Cargo-raze prefixes binary targets with cargo_bin_, as although Cargo permits binaries and libraries to share the same target name, Bazel disallows this.

Crates that only provide binaries

Currently, cargo does not gather metadata about crates that do not provide any libraries. This means that these specifying them in the [dependencies] section of your Cargo.toml file will not result in generated Bazel targets. Cargo-raze has a special field to handle these crates when using genmode = "Remote":

[package.metadata.raze.binary_deps]
wasm-bindgen-cli = "0.2.68"

In the snippet above, the wasm-bindgen-cli crate is defined as binary dependency and Cargo-raze will ensure metadata for this and any other crate defined here are included in the resulting output directory. Lockfiles for targets specified under [package.metadata.raze.binary_deps] will be generated into a lockfiles directory inside the path specified by workspace_path.

Note that the binary_deps field can go in workspace and package metadata, however, only one definition of a binary dependency can exist at a time. If you have multiple packages that depend on a single binary dependency, that definition needs to be be moved to the workspace metadata.

Build scripts by default

Setting default_gen_buildrs to true will cause cargo-raze to generate build scripts for all crates that require them:

[package.metadata.raze]
workspace_path = "//cargo"
genmode = "Remote"
default_gen_buildrs = true

This setting is a trade-off between convenience and correctness. By enabling it, you should find many crates work without having to specify any flags explicitly, and without having to manually enable individual build scripts. But by turning it on, you are allowing all of the crates you are using to run arbitrary code at build time, and the actions they perform may not be hermetic.

Even with this setting enabled, you may still need to provide extra settings for a few crates. For example, the ring crate needs access to the source tree at build time:

[package.metadata.raze.crates.ring.'*']
compile_data_attr = "glob([\"**/*.der\"])"

If you wish to disable the build script on an individual crate, you can do so as follows:

[package.metadata.raze.crates.some_dependency.'*']
gen_buildrs = false

FAQ

Why choose Bazel to build a Rust project?

Bazel ("fast", "correct", choose two) is a battle-tested build system used by Google to compile incredibly large, multilingual projects without duplicating effort, and without compromising on correctness. It accomplishes this in part by limiting what mechanisms a given compilation object can use to discover dependencies and by forcing buildable units to express the complete set of their dependencies. It expects two identical sets of build target inputs to produce a byte-for-byte equivalent final result.

In exchange, users are rewarded with a customizable and extensible build system that compiles any kind of compilable target and allows expressing "unconventional dependencies", such as Protobuf objects, precompiled graphics shaders, or generated code, while remaining fast and correct.

Its also probable (though not yet demonstrated with benchmarks) that large applications built with Bazel's strengths in mind: highly granular build units, will compile significantly faster as they are able to cache more aggressively and avoid recompilation of as much code while iterating.

Why try to integrate Cargo's dependencies into this build tool?

For better or worse, the Rust ecosystem heavily depends on Cargo crates in order to provide functionality that is often present in standard libraries. This is actually a fantastic thing for the evolution of the language, as it describes a structured process to stabilization (experimental crate -> 1.0 crate -> RFC -> inclusion in stdlib), but it means that people who lack access to this ecosystem must reinvent many wheels.

Putting that aside there are also fantastic crates that help Rust developers interact with industry standard systems and libraries which can greatly accelerate development in the language.

Why not build directly with Cargo / Why generate rustc invocations?

Though the burden of emulating Cargo's functionality (where possible at all!) is high, it appears to be the only way to maintain the guarantees (correctness, reproducibility) that Bazel depends on to stay performant. It is possible and likely with inflight RFCs that Cargo will become sufficiently flexible to allow it to be used directly for compilation but at this point in time it appears that maintaining a semblance of feature parity is actually easier than avoiding all of the sharp edges introduced by treating Cargo like the Rust compiler.

What is buildable right now with Bazel, and what is not?

With a little bit of elbow grease it is possible to build nearly everything, including projects that depend on openssl-sys. Many sys crates will require identifying the system library that they wrap, and either vendoring it into the project, or telling Bazel where it lives on your system. Some may require minor source tweaks, such as eliminating hardcoded cargo environment variable requirements. Fixes can be non-trivial in a few cases, but a good number of the most popular crates have been built in an example repo, available at https://github.com/acmcarther/cargo-raze-crater

Example Repos

See these examples of providing crate configuration:

Using vendored mode:

Using remote mode:

Compiling OpenSSL:

The [package.metadata.raze] section is derived from a struct declared in impl/src/settings.rs.

cargo-raze's People

Contributors

a1ph avatar acmcarther avatar arm1stice avatar atadau avatar boxdot avatar bsilver8192 avatar bspeice avatar dae avatar damienmg avatar dependabot[bot] avatar dfreese avatar djmarcin avatar gregbowyer avatar illicitonion avatar johnedmonds avatar keith avatar kornholi avatar mfarrugi avatar mstg avatar ok32 avatar piotrsikora avatar rickwebiii avatar sayrer avatar sitaktif avatar smklein avatar uebelandre avatar vitalyd avatar vmax avatar wcalandro avatar wchargin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cargo-raze's Issues

Bazel builds of packages error out on recent bazelbuild/rules_rust with "Option 'target' given more than once"

When trying to build the example rust workspace with Bazel and using recent versions of the bazelbuild/rules_rust repo,

bazel  build --verbose_failures //vendored/hello_cargo_library

Results in an error: error: Option 'target' given more than once

Works on the commit previous to this one:
bazelbuild/rules_rust@b3c8bad

ERROR: /home/matt/dev/cargo-raze/examples/vendored/hello_cargo_library/cargo/vendor/cfg-if-0.1.2/BUILD:27:1: error executing shell command: '/bin/bash -c set -e; if [ ! -z "${TMPDIR+x}" ]; then mkdir -p $TMPDIR; fi; rm -rf bazel-out/k8-fastbuild/bin/vendored/hello_cargo_library/cargo/vendor/cfg-if-0.1.2/cfg_if.deps; mkdir bazel-out/k8-f...' failed (Exit 101): bash failed: error executing command 
  (cd /home/matt/.cache/bazel/_bazel_matt/66f27ff99aab57cee57e8b3b1584c611/execroot/io_bazel_rules_rust && \
  exec env - \
    PATH=/home/matt/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/matt/bin \
  /bin/bash -c 'set -e; if [ ! -z "${TMPDIR+x}" ]; then mkdir -p $TMPDIR; fi; rm -rf bazel-out/k8-fastbuild/bin/vendored/hello_cargo_library/cargo/vendor/cfg-if-0.1.2/cfg_if.deps; mkdir bazel-out/k8-fastbuild/bin/vendored/hello_cargo_library/cargo/vendor/cfg-if-0.1.2/cfg_if.deps
 CARGO_PKG_VERSION=0.1.2 CARGO_PKG_VERSION_MAJOR=0 CARGO_PKG_VERSION_MINOR=1 CARGO_PKG_VERSION_PATCH=2 CARGO_PKG_VERSION_PRE= CARGO_PKG_AUTHORS= CARGO_PKG_NAME=cfg_if CARGO_PKG_DESCRIPTION= CARGO_PKG_HOMEPAGE= OUT_DIR=$(pwd)/out_dir external/rust_linux_x86_64/bin/rustc vendored/hello_cargo_library/cargo/vendor/cfg-if-0.1.2/src/lib.rs --crate-name cfg_if --crate-type lib --codegen opt-level=0 --codegen debuginfo=0 --codegen metadata=-1519635916 --codegen extra-filename='\''-1519635916'\'' --codegen ar=/usr/bin/ar --codegen linker=/usr/bin/gcc --codegen link-args='\'''\'' --out-dir bazel-out/k8-fastbuild/bin/vendored/hello_cargo_library/cargo/vendor/cfg-if-0.1.2 --emit=dep-info,link --color always --target=x86_64-unknown-linux-gnu --cap-lints allow --target=x86_64-unknown-linux-gnu')

Use --sandbox_debug to see verbose messages from the sandbox
error: Option 'target' given more than once

Question: Why not generate BUILD.bazel like other configuration generators?

Hello!
This may be a "corner case" situation, but given other tools like Gazelle is generating BUILD.bazel, why isn't cargo-raze doing the same?

This would especially be helpful when using a case insensitive file system (Windows + WSL) considering some crates (openssl-sys) can have a directory called build and that makes cargo-raze fail with the following error message:

error: Is a directory (os error 21)

One can circumvent this problem by manually changing and recompiling, but maybe it'll be better to change it all together to BUILD.bazel. Any thoughts?

Move to the official bazelbuild/rules_rust?

Reraising @bspeice's issue from #11

The reason that this isn't in rules_rust was that I wasn't sure that this was the right solution for everyone to pursue. Solutions to Cargo-Bazel build system integration seems to lie on a spectrum between "let Cargo make decisions" and "let Bazel make decisions", and this project is decidedly in the latter camp.

Most folks I've talked to on both sides expect Cargo to play a much larger role than it plays in this implementation -- it just turned out in practice that it was easier for me to get code compiling by structuring the integration in the manner that I did. That said, I've been averse to imply that cargo-raze is the way an integration "must be".

Its possible that an implementation similar to gazelle that treats BUILD files monorepo-wide as generated artifacts of Cargo.tomls would be more compatible with the ecosystem. I personally don't like the development experience that Cargo provides though, so this was as much an escape from that workflow as it was a compatiblity project.


Footnote: My issues are primarily:

  • The somewhat artificial notion of a "project".
  • "Cargo.toml" files being mandatory per "project" (I can't fault the ecosystem here, its necessary in the project model)
  • "src/" as a thing. If every piece of code in my project had to be under "src", i'd be wearing out my 's', 'r', and 'c' keys while navigating the filetree in my terminal.
  • Cargo+Rust's co-opting of the directory structure. As a practice, I don't let rust_libraries bring in stuff from nested directories in the form
mod expected_lib;
// Automagically bring in expected_lib/mod.rs

Support non-standard registries in remote mode

Remote GenMode is currently masquerading as if it supports arbitrary registries, but in reality it only supports the standard registry due to how it is pulling down the crates:

url = "https://crates-io.s3-us-west-1.amazonaws.com/crates/{{crate.pkg_name}}/{{crate.pkg_name}}-{{crate.pkg_version}}.crate",

Normally, this should be using something derived from the registry url, but there is a weird issue described in ancient times here: acmcarther/cargo-raze-vendor#3

Tracking crates with exotic build.rs

Moving acmcarther/cargo-raze#16 over here.

Bazel needs some extra help with build.rs scripts that:

  • link cc libs to *-sys crates
  • use exotic cargo environment variables
  • use compile time file includes
  • produce --cfg flags
  • and do other bespoke logic

Conveniently I haven't run into many additional problems, and our set of overrides hasn't grown.

Not all binaries have a library to depend on (can't build ripgrep)

Added

ripgrep = "0.10.0"

...

[raze.crates.ripgrep.'0.10.0']
extra_aliased_targets = [
    "cargo_bin_rg"
]

to my Cargo.toml, but get an error when trying to build :cargo_bin_rg because it depends on a library target that doesn't exist.


# Binaries get an implicit dependency on their lib
":{{crate.pkg_name | replace(from="-", to="_") }}",

This ought to be a little smarter.. any suggestions?

Feature request: Autodetect target

Without setting the target setting in Cargo.toml I got the very confusing error "error[E0461]: couldn't find crate core with expected target triple x86_64-unknown-linux-gnu" on my Mac. This was confusing, because neither Cargo nor rules_rust required explicit target setting, and so having to work out that I had to explicitly set it for things to not fail horribly was confusing. Also, this now means I've gone from a cross-platform build to a single-platform build, which is somewhat suboptimal.

Thanks for building the tool!

if name not in native.existing_rules()

Should emit this by default as the guard for repository rule declarations to play nice with loading from multiple crates.bzl that have overlapping names`

For example:

def raze_fetch_remote_crates():
    existing = native.existing_rules()
    if "raze__aho_corasick__0_6_8" not in existing:
        native.new_http_archive(
            name = "raze__aho_corasick__0_6_8",
            url = "https://crates-io.s3-us-west-1.amazonaws.com/crates/aho-corasick/aho-corasick-0.6.8.crate",
            type = "tar.gz",
            strip_prefix = "aho-corasick-0.6.8",
            build_file = "//cargo/remote:aho-corasick-0.6.8.BUILD"
        )

0.0.19 Seems to pull latest versions from crates.io

Bindgen on crates.io seems to have had a version bump, that bump gives a failure as seen in #19.

However, seperate to that with a Cargo.toml like so

[package]
name = "rust_deps"
version = "0.1.0"

[lib]
path = "fake_lib.rs"

[dependencies]
#backtrace = "0.3"
bindgen = "0.32.2"

[raze]
# The WORKSPACE relative path to the Cargo.toml working directory.
workspace_path = "//third_party/rust"
# The target to generate BUILD rules for.
target = "x86_64-unknown-linux-gnu"
# Have Bazel pull the dependencies down
genmode = "Remote"

[raze.crates.bindgen.'0.32.2']
## needed to build bindgen
gen_buildrs = true
extra_aliased_targets = [
  "cargo_bin_bindgen"
]

cargo raze appears to make a build script for bindgen 0.32.2

output_buildfile_suffix = "BUILD.bazel" does not work

INFO: Call stack for the definition of repository 'raze__bls_aggregates__0_6_1':
 - /home/preston90/bls-benchmarks/cargo/crates.bzl:15:9
 - /home/preston90/bls-benchmarks/cargo/crates.bzl:72:5
 - /home/preston90/bls-benchmarks/WORKSPACE:49:1
ERROR: /home/preston90/bls-benchmarks/cargo/BUILD.bazel:11:1: no such package '@raze__bls_aggregates__0_6_1//': Traceback (most recent call last):
        File "/home/preston90/.cache/bazel/_bazel_preston90/5ab6d69733459142a1e1f37eacad08ea/external/bazel_tools/tools/build_defs/repo/git.bzl", line 228
                workspace_and_buildfile(ctx)
        File "/home/preston90/.cache/bazel/_bazel_preston90/5ab6d69733459142a1e1f37eacad08ea/external/bazel_tools/tools/build_defs/repo/utils.bzl", line 64, in workspace_and_buildfile
                ctx.symlink(ctx.attr.build_file, "BUILD.bazel")
Not a regular file: /home/preston90/bls-benchmarks/cargo/remote/bls-aggregates-0.6.1.BUILD and referenced by '//cargo:bls_aggregates'

Link 49 of the workspace is raze_fetch_remote_crates(). Something is still referencing the default BUILD file path.

CrateContext.features needs to be specialized using the target_triple

This is something I've harped on several times:

#52
bazelbuild/rules_rust#100

(details discussed in rust-lang/cargo#5122 )

... But this is actually implemented incorrectly at the moment. Right now, we're just "trusting" the target_triple agnostic features[1]. Instead, we need to specialize them to match the dependency specialization[2].

I'm going to do this in the multi target PR that I'm cooking up right now, but I wanted a tracking issue to use for my TODOS so here it is.

[1]

features: node.features.clone().unwrap_or(Vec::new()),

[2]
let platform = try!(Platform::from_str(target_str));
// Skip this dep if it doesn't match our platform attributes
if !platform.matches(&settings.target, Some(&platform_attrs)) {
continue;
}

Produce a proposal for composable workspaces

I'm not really sure how widely I'd like this scoped but the reality is that there are workspace-level projects that will depend on something like cargo-raze to synthesize a list of dependencies for use external to the workspaces themselves. These use cases will need a real answer to the composition problem.

Specific examples:
bazelbuild/rules_rust#92
pubref/rules_protobuf#226

In part, this will involve deferring generation of exact rust_library decls behind some sort of middle layer. This hits an issue intrinsic to the problem space though -- there isn't a perfect place in the Bazel build lifecycle for the kind of dependency resolution that Cargo likes to do. The hardest problems that I see are that some logic can be performed pre-loading phase (ala cargo-vendor), but some ought to happen once the target platform is known. Cargo's implementation of target platform handling requires parsing a target specification grammar which I was disinclined to duplicate (see CfgExpr in Cargo for details).

I discussed some of this in brief @ rust-lang/rfcs#2136 (comment) and throughout that issue.

Build with bazel?

Given that this project is a bazel build file generator, seems like it should build with bazel. Specifically, I'd like to pull down and reference a rust_binary without having to use crates.io / cargo itself!.

Set RUSTC environmental for build scripts

Some crates, like cargo_version (https://github.com/Kimundi/rustc-version-rs/blob/master/src/lib.rs#L126) use this environment variable to figure out which rustc is running. This crate is for example a dependency for rusoto.

Changing the auto generated rusoto_core genrule to settings this variable using export RUSTC=\"$$PWD/$(location @rust_linux_x86_64//:rustc)\";" and adding it in the tools section worked for me, but maybe this variable should always be set?

Suppress stderr output of `build.rs`

My fellow engineers at Lyft complain that building the rust stuff generates a ton of noisy output on the command line. The reason is that when cargo-raze runs build.rs, it still echos all the stderr these scripts generate - though of course bazel is ignoring them entirely.

Can we suppress the output of stderr/stdout when we run these scripts?

rustc_demangle 0.1.8 generates invalid BUILD file

"""
cargo-raze crate build file.

DO NOT EDIT! Replaced on runs of cargo-raze
"""
package(default_visibility = ["//ext/public/rust/cargo:__subpackages__"])

load(
    "@io_bazel_rules_rust//rust:rust.bzl",
    "rust_library",
    "rust_binary",
    "rust_test",
    "rust_bench_test",
)


rust_binary(
    name = "rustc_demangle",
    crate_root = "src/main.rs",
    srcs = glob(["**/*.rs"]),
    deps = [
        
        
    ],
    rustc_flags = [
        "--cap-lints allow",
        "--target=x86_64-unknown-linux-gnu",
        
    ],
    
    crate_features = [
        
    ],
)
rust_library(
    name = "rustc_demangle",
    crate_root = "src/lib.rs",
    crate_type = "lib",
    srcs = glob(["**/*.rs"]),
    deps = [
        
        
    ],
    rustc_flags = [
        "--cap-lints allow",
        "--target=x86_64-unknown-linux-gnu",
        
    ],
    
    crate_features = [
        
    ],
)

Both targets end up w/ the same name.. don't have time to chase this down right now.

My work around was to comment out the binary target.

Compiling tera

The tera templating library is a pretty tricky crate to get working under bazel. This issue could probably be considered a sub-issue of #61 since cargo-raze depends on tera as well, but I'm opening a separate issue to avoid muddying those waters. It also requires handling some of the same obstacles (proc_macro2, lazy_static!) as #64, so perhaps the answer will be generally applicable for more users than just me. I can get it to compile, but it involves some absolute path hackery within source of the pest_generator crate. I'm hoping you can point me to a better solution - that is, even if I still need to make minor source tweaks, at least one which doesn't rely on a hardcoded path.

Here's what I've got so far:

In ~/demo/cargo/Cargo.toml:

[package]
name = "cargo"
version = "0.1.0"
authors = ["Chuck Bassett <[email protected]>"]

[dependencies]
tera = "=0.11.18"

[raze]
genmode = "Remote"
target = "x86_64-unknown-linux-gnu"
workspace_path = "//cargo"

[raze.crates.deunicode.'0.4.3']
data_attr = "[\"src/mapping.txt\", \"src/pointers.bin\"]"

[raze.crates.unic-ucd-version.'0.7.0']
data_attr = "[\"tables/unicode_version.rsv\"]"

[raze.crates.unic-ucd-segment.'0.7.0']
data_attr = "glob([\"tables/*.rsv\"])"

[raze.crates.proc-macro2.'0.4.20']
additional_flags = [
  "--cfg use_proc_macro"
]

[raze.crates.tera.'0.11.18']
data_attr = "[\"src/parser/tera.pest\"]"

[raze.crates.lazy_static.'1.1.0']
additional_flags = [
  "--cfg lazy_static_inline_impl"
]

The toml above gets you almost all the way there. The problem is that tera includes the same tera.pest file twice. Once via an include_str! which is handled by the above, but then via a procedural macro:

In tera/src/parser/mod.rs:

const _GRAMMAR: &str = include_str!("tera.pest");

#[derive(Parser)]
#[grammar = "parser/tera.pest"]
pub struct TeraParser;

Looking into the pest-generator crate, it is erroring out when it cannot find the file ./src/parser/tera.pest at pest/generator/src/lib.rs#L48. By editing the derive_parser function to explicitly open tera.pest by absolute path I can make the compilation succeed, but this is obviously The Wrong Way. Can you point me toward the right way to do this? I'd be glad to open a PR to include it as an example project if that'd be helpful.

Looks like multiple versions of a crate with a build script clobber each other

cmd = "mkdir -p {{ crate_name_sanitized}}_out_dir_outputs/;"

This ends up making that dir in $execroot/mime_guess_out_dir_outputs, and we have two versions of mime_guess, and it appears they're clobbering each other.

We should have a better idea if that's true and a fix for it in a little bit.

cc @vitalyd

Edit:
I imagine this is partly because sandboxing is disabled for these genrules.

Cargo workspaces interfere with cargo raze

I have cargo workspaces setup in my project for the rust rls extension in vscode along side Bazel.

If I try to follow the setup instructions for vendoring I get:

Loaded override settings: RazeSettings {
    workspace_path: "//cargo",
    target: "x86_64-unknown-linux-gnu",
    crates: {},
    gen_workspace_prefix: "raze",
    genmode: Vendored,
    output_buildfile_suffix: "BUILD.bazel",
}
error: Raze failed to render with cause: "Failed to find expected vendored crates in Some("/home/ironpeak/Projects/dungeon-core/cargo/./vendor/"): ["logical-0.1.0", "engine-0.1.0", "entities-0.1.0", "parser-0.1.0", "arithmetic-0.1.0"] and 3 others. Did you forget to run cargo-vendor?"

I have found that I can successfully run cargo raze if I delete the Cargo.toml workspace and follow the instructions.

Unify remote and vendored template files

Splitting them out was a hack for supporting remote dependencies.

CLs like #33 expose how ugly of a hack it really was.

It should be possible to resolve this by using a template per-(remote/vendor) to supply a function to use to resolve the dependency path.

Crates with native dependencies need missing environment flags for buildscripts

The widely used cc-rs crate depends on the OPT_LEVEL and HOST environment flags and will crash if these are not present.
These should be provided to the genrule cmd for the build_script_executors of child packages.

At the moment, it looks like rules_rust knows about the opt_level but I don't know how cargo raze would be able to know that at generation. Is this possibly exposed via something that could be loaded from rules_rust for instantiating the genrule?

Support git dependencies

I have a git-based dependency in one of my projects that doesn't seem to get picked up by cargo-raze.

hybrid-clocks = { git = "https://github.com/palfrey/hybrid-clocks.git", branch = "serde-0.9", features = ["serde"] }

Admittedly, in this case, I think I can just upgrade to the latest version from crates.io now, but it would be good to support the general case.

Issues installing the crate

I always get the following error when trying to install via cargo install cargo-raze:

error: failed to run custom build command for `openssl v0.9.24`                                                                                                                                  
process didn't exit successfully: `/tmp/cargo-install7rJjih/release/build/openssl-30a87315a59668e1/build-script-build` (exit code: 101)
--- stderr
thread 'main' panicked at 'Unable to detect OpenSSL version', /home/sven/.cargo/registry/src/github.com-1ecc6299db9ec823/openssl-0.9.24/build.rs:16:14
note: Run with `RUST_BACKTRACE=1` for a backtrace.

warning: build failed, waiting for other jobs to finish...
error: failed to compile `cargo-raze v0.1.1`, intermediate artifacts can be found at `/tmp/cargo-install7rJjih`                                                                                  

Caused by:
  build failed

Not sure if that matters, but I have openssl installed. Strange thing is, I wanted to update cargo-raze, so I uninstalled it first. I could install it the first time without any problems. But now...

If you need any log files or other things, let me know.

Automate smoke testing

Due to the deficient test suite, when I'm preparing to submit code to google/cargo-raze I run a smoke test against cargo-raze-examples. The steps look roughly like this:

# From working directory
$ cargo install -f
$ git clone [email protected]:acmcarther/cargo-raze-examples
$ cd cargo-raze-examples/internal && ./evaluate-local-raze.sh

This runs cargo-raze across a swath of configurations and attempts to build them. If we run Travis in sudo mode, it should be possible to do this in CI -- though this will then require:

  1. The rust tests to be run "manually" (instead of automagicially, as they are now)
  2. bazel to be installed before build

compiling terminfo

I'm trying to pull in @damienmg's starlark crate and am seeing the folllowing issue.

Her is my Cargo.toml file:

[package]
name = "fake_lib"
version = "0.0.1"

[lib]
path = "lib.rs"

[dependencies]
starlark = "0.1.2"

[raze]
genmode = "Remote"
workspace_path = "//cargo"

When the terminfo dependency is compiled, I see the following error:

INFO: Analysed target //:main (1 packages loaded).
INFO: Found 1 target...
ERROR: /home/pcj/.cache/bazel/_bazel_pcj/9feaf001454c16e46c01ade0d88992bb/external/raze__terminfo__0_6_1/BUILD.bazel:23:1: error executing shell command: '/bin/bash -c set -e; if [ ! -z "${TMPDIR+x}" ]; then mkdir -p $TMPDIR; fi; rm -rf bazel-out/k8-fastbuild/bin/external/raze__terminfo__0_6_1/terminfo.deps; mkdir bazel-out/k8-fastbuild/bin/external/...' failed (Exit 101)
error: couldn't read "/home/pcj/.cache/bazel/_bazel_pcj/9feaf001454c16e46c01ade0d88992bb/sandbox/linux-sandbox/1/execroot/__main__/out_dir/names.rs": No such file or directory (os error 2)
  --> external/raze__terminfo__0_6_1/src/names.rs:15:1
   |
15 | include!(concat!(env!("OUT_DIR"), "/names.rs"));
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

Any suggestions?

Unable to use cargo-raze when vendored to subdirectory

Howdy!

I'm just trying to get started with this and explore this as a viable path from using cmake and cargo to using Bazel for a mixed C++ and Rust monorepo.

Right out of the gate though I'm running into troubles. I've run cargo vendor -x vendor/crates. I'm using the 'vendor' prefix because this is where we've already established our location for vendored C and C++ libraries.

I've created a WORKSPACE file set up for rust, I add the following to my Cargo.toml (which is the root of a cargo workspace):

[raze]
workspace_path = "//vendor/crates"

When I run cargo raze I get the following panic:

Loaded override settings: RazeSettings {
    workspace_path: "//vendor/crates",
    target: "x86_64-unknown-linux-gnu",
    crates: {},
    gen_workspace_prefix: "raze",
    genmode: Vendored
}
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: CargoError(Msg("failed to find ./vendor/vcpkg-0.2.4/. Either switch to \"Remote\" genmode, or run `cargo vendor -x` first."), State { next_error: None, backtrace: None })', libcore/result.rs:945:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

For some reason it doesn't appear to be using the crates subdirectory. This is a completely vanilla repo as far as bazel is concerned. I'm attempting to some of the less complicated rust targets building before stepping off into the C++ side.

Add build_file_path [raze.crate.version] override that supports providing a custom build file

This is a pretty finicky proposition, all told. Dependencies are generally versioned and those versions can change due to non-local causes which would require manually updating the build file. One example of such a cause would be newly including a dependency that forces another dependency to downgrade.

The cleanest implementation of this would somehow allow the BUILD file author to refer to dependencies "at whatever version they would otherwise be" via an exported BZL file or something else.. An initial implementation could forego these niceties though.

cc: @mfarrugi

Add flag to enable/disable sandboxing of build scripts

Build script execution genrules currently set local=1. tags = ["no-sandbox"] is narrower than local=1 and was the original intent, so I'll switch it to that for the time being (#105), but ultimately, I think disabling sandboxing should not be the default, but rather a crate option.

see also #100

Support for targeting multiple architectures

(Moving from: acmcarther/cargo-raze#33)
It would be great if there was a straightforward way to add support for multiple architectures at once. A bit like how the Bazel --ios_multi_cpus works, but perhaps just allow an array of targets in Cargo.toml, which generates arch specific rules.

Answer from @acmcarther:

"I'm certainly open to this, but each target generates features, build scripts, and dependencies that are different, and I haven't had a chance to give enough though to the best way to organize the generated files. Do you have any ideas in that respect?"

Log 0.4.8 and 0.3.9 generation issue --cfg=atomic_cas

hey @acmcarther, not sure if that is problem of cargo raze or rules_rust but the recent dependency on log 0.3.9 fails to build. Example that indirectly depends on that log version is slogger or grpc-0.6.1 crates from crates.io.

The problem seems to lay that log-0.3.9 imports log-0.4.8 which core method set_logger depends on atomic operations that are flipped via atomic_cas cfg flag. That one doesn't seem to be passed on many platforms correctly during compilation time. (Tested on Ubuntu 18.04 and Arch Linux) .

Workaround is to force cfg flag with cargo raze.

[raze.crates.log.'0.4.8']
additional_flags = [
    "--cfg=atomic_cas"
]

Not sure if you or rules_rust are aware of this issue. That's why I am creating this post. Also for any other person dealing with similar problem as me.

Derive builder does not build

Needs markdown file as "include_str".

data = glob(["**/*"])

should do the trick (not sure the side effects though).

Support BUILD gen for rust-libloading

libloading recently made a change to bundle C file to have a weak mutex:
nagisa/rust_libloading#32

This is a corner case of raze -- its not really possible to either

  1. Run the build script and scoop up the build object file

OR

  1. Override via raze.crate settings what it would have done, without just overriding the whole crate.

Furthermore, the build script itself depends on some flags that I wasn't aware of:

    let target_os = env::var("CARGO_CFG_TARGET_OS");
    let is_unix = env::var_os("CARGO_CFG_UNIX").is_some();

For now, I'm overriding the whole crate. This exposes an ergonomic painpoint: It should be possible to workspace-wide replace one target with another.


As a side note, I would benefit from this particular attr being stabilized, but it doesn't seem like that will happen any time soon:
rust-lang/rust#29603

That is to say, the small-ish missing stable feature of specifying weak linkage led to rust-libloading bringing in C code to replace it, which is hard to automatically detect and build on my end. What they had before (lazy_static) worked in 99.9% of circumstances, until some other random feature required them to up their major version, dramatically increasing the chances of the crate being linked twice (which would potentially lead to bad behavior when using lazy_static).

Yikes.

Interaction with Rust workspaces

I've got a project (https://github.com/palfrey/potboiler) I'm testing out using cargo-raze to build, but it uses Cargo workspaces to separate sub-projects and that doesn't seem to interact well with cargo-raze. I've added a top-level WORKSPACE and the generated BUILD for a Remote genmode only seems to contain the dependencies from one of the sub-folder Cargo.toml's. Experimentation seems to indicate it's whichever one is listed first in the [workspace] "members" key.

Any thoughts on how to get this to read from all of them?

Fails to build on systems with OpenSSL 1.1.1

Moved from acmcarther/cargo-raze#34

Per @alaroldai


Related: sfackler/rust-openssl#987

Because cargo-raze depends on an old version of cargo (v0.20.0, current version is v0.30.0), which in turn depends on an old version of openssl, cargo-raze fails to build on systems with OpenSSL v1.1.1 installed because the openssl crate fails to detect the installed OpenSSL version.

I've tried fixing this locally by updating the dependency on Cargo, but it looks like there are some breaking API changes that make that a more complicated process than I expected. Someone more familiar with the Cargo API might have a better shot at this.

In case it's related, here's some system info:
OS: Arch Linux (Linux v4.18.12)
OpenSSL version: 1.1.1 (11 Sep 2018)
Version of the Cargo binary I used to build cargo-raze: 1.29.0

Here's a sample of the build output after updating the version of cargo:


I've observed that the main issues are

  1. Changes in CLI handling
  2. Replacement of rustc-serialize with serde
  3. Changes in error handling to the failure crate

There are other miscellaneous small changes in param types or fields as well.

I'm looking at this now, but might not have a PR out immediately as I learn about these new APIs.

Is there a reason build_scripts does not include flags?

Hey!

Currently some crates has conditional behaviour in their build scripts that can be specified with --cfg flags. Currently cargo-raze does not pass on flags to the build_script targets. Is there a reason for that?

Just wanted to check in, I can create a PR if it's acceptable to pass them to the build_script target.

Update released version for #73?

Would it be possible to release a new version on crates.io for #73.

We recently updated rules_rust and now raze makes files that are slightly broken

Bindgen 0.32.3 compile failure

I am not sure where this is hiding, but we might need to add host-target.txt into the sandbox somehow?

greg@gregslaptop ~/that-thing/third_party/rust $ bazel build '//third_party/rust:xbindgen'
INFO: Analysed target //third_party/rust:xbindgen (1 packages loaded).
INFO: Found 1 target...
ERROR: /home/greg/.cache/bazel/_bazel_greg/67d8da3589d026fae94b8bebe7cc20ac/external/raze__bindgen__0_32_3/BUILD.bazel:55:1: error executing shell command: 'set -e; rm -rf bazel-out/k8-fastbuild/bin/external/raze__bindgen__0_32_3/bindgen.deps; mkdir bazel-out/k8-fastbuild/bin/external/raze__bindgen__0_32_3/bindgen.deps
 ln -sf ../../raze__cexpr__0_2_3/...' failed (Exit 101)
error: couldn't read /home/greg/.cache/bazel/_bazel_greg/67d8da3589d026fae94b8bebe7cc20ac/bazel-sandbox/5232548725936954145/execroot/io_biasedunit/out_dir/host-target.txt: No such file or directory (os error 2)
   --> external/raze__bindgen__0_32_3/src/ir/context.rs:526:9
    |
526 |         include_str!(concat!(env!("OUT_DIR"), "/host-target.txt"));
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

Target @raze__bindgen__0_32_3//:cargo_bin_bindgen failed to build
Use --verbose_failures to see the command lines of failed build steps.

Generate //cargo:cargo.bzl during WORKSPACE

Having both Cargo.toml and //cargo:crates.bzl checked in forks the source of truth in undesirable ways. There is a trick that rules_python is using to avoid this split, which would enable Bazel to directly consume Cargo.toml (or preferably Cargo.lock).

The pattern is effectively a two-phase load:

load("@somewhere//something:cargo.bzl", "cargo_import")

# This would run cargo-raze to produce @the_repo_name//:crates.bzl
cargo_import(
    name = "the_repo_name",
    file = "Cargo.lock",
)

# Actually evaluate the generated skylark
load("@the_repo_name//:crates.bzl", "raze_fetch_remote_crates")
raze_fetch_remote_crates()

This is basically identical to what you are doing, but avoids checking in crates.bzl by running it during WORKSPACE iff Cargo.lock changes (no more forking the source of truth!).

I also notice that this generates repository names containing exact versions, which could lead to significant churn when dependencies update (e.g. update toml => lock). The way rules_python addresses this is by generating a function in the_repo_name called requirement() that maps the requirement's name to the py_library we generate for it. This enables BUILD files to avoid the versioned dependency names (see example).

[docs] `cargo vendor -x` no longer works replace with `cargo-vendor vendor -x`

I have run the cargo vendor and it has created directories where I want them however they don't have a version suffix which seems required by this tool.

/workspace/src/lwebco.de/third_party/cargo $ 
/workspace/src/lwebco.de/third_party/cargo $ ls vendor/
linked-hash-map log             yaml-rust
/workspace/src/lwebco.de/third_party/cargo $ cat Cargo.toml 
[package]
name = "lwebco_de"
version = "0.1.0"

[lib]
path = "fake_lib.rs"

[dependencies]
log = "=0.3.6"
yaml-rust = "0.4.3"

[raze]
workspace_path = "//third_party/cargo"
target = "x86_64-unknown-linux-gnu"

You can see the output below when I try to run cargo raze.

$ cargo raze
Loaded override settings: RazeSettings {
    workspace_path: "//third_party/cargo",
    target: "x86_64-unknown-linux-gnu",
    crates: {},
    gen_workspace_prefix: "raze",
    genmode: Vendored,
    output_buildfile_suffix: "BUILD",
}
error: Raze failed to render with cause: "Failed to find expected vendored crates in Some("/workspace/src/lwebco.de/third_party/cargo/./vendor/"): ["yaml-rust-0.4.3", "linked-hash-map-0.5.2", "log-0.3.6"]. Did you forget to run cargo-vendor?"   

Versions

$ rustc --version
rustc 1.37.0 (eae3437df 2019-08-13)
$ cargo --version
cargo 1.37.0 (9edd08916 2019-08-02)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.