Coder Social home page Coder Social logo

cargo-bundle's People

Contributors

0x61nas avatar actuallyhappening avatar aerkiaga avatar andrewdavidmackenzie avatar andrewrademacher avatar arturkovacs avatar body20002 avatar burtonageo avatar daniel-abramov avatar frisoft avatar goingforbrooke avatar helloimalemur avatar jakobhellermann avatar kalissaac avatar kianmeng avatar kingtous avatar kneelawk avatar lord avatar madsmtm avatar markisha64 avatar mdsteele avatar mixator avatar oluseyi avatar paulrouget avatar rhysd avatar simlay avatar slonopotamus avatar smntin avatar tmpfs avatar tweirtx 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-bundle's Issues

Generates non-reproducible Installed-Size field

[ Stumbled over this, and did a quick review over the implementation for conformance issues. :) ]

The generated Installed-Size field appears to be created using an out of date algorithm, which produces unreproducible values depending on the file system type and settings.

The current algorithm used by dpkg-gencontrol is described in the deb-substvars(5) man page, the Debian policy is currently out of date, see the following Debian policy bug for the reference to the discussion that prompted the change.

Consider refactoring of config loading

It would be nice to use serde and serde-derive crates in order to write the TOML config parsing in an elegant way (without the boilerplate code and self-written macro rules).

Having serde-derive would allow usage of #[derive(Deserialize)] on the corresponding config structures, so the parsing would as simple as let config: Settings = toml::from_str(buffer)?;

Track valid Cargo.toml format

While implementing workspace awareness, I have realized that the definition of CargoSettings does not contemplate all fields of a Cargo.toml file.

e.g. a Cargo.toml file with a "workspace" table broke the deserialization as an unexpected field.
e.g. when parsing 'parent' Cargo.toml files in a workspace setup) the lack of a package table broke it.

I suggest we add some tests to test Cargo.toml deserialization against valid Cargo formats with newer fields and lacking some optional ones.

Ideally (I haven't checked) the Cargo project would offer a crate that defines the struct and deserialization using serde, then we could all use it directly and track updates.

Support for OS-specific "bundle values"

Bundles for some operating systems may have more options available than for others.

For instance if we're talking about OS X, it has several properties which you can specify in Info.plist such as CFBundleURLName (quite a common thing if you want your app to be able to catch the corresponding URI requests), at the moment there is no way to add additional properties to Info.plist as it has a fixed, hardcoded format with only certain (the most common) values.

It would be nice to be able to specify OS-specific bundle properties.

Error: the source path is not an existing regular file

Have a project with this structure:

screen shot 2018-02-04 at 14 31 05

and this Cargo.toml:

[[bin]]
name = "flowc"
path = "src/flowc.rs"

[package.metadata.bundle]
identifier = "net.mackenzie-serres.flow.flowc"
icon = ["icons/32x32.png", "icons/128x128.png"]
resources = ["resources"]

(parts removed for brevity)

when I run "cargo bundle" I get this error:

cargo bundle
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
    Bundling flowc.app
Error: the source path is not an existing regular file

Any ideas of what is wrong?

Support zip and tar.gz bundles

I suggest extending cargo-bundle with generating also ZIP and tar.gz acrhive bundles.
For many applications, espectially portable (like many of rust binaries are) it's
quite convenient way to deploy the binaries over installers
(which may require user to have root/admin permissions).

The Cargo.toml configuration section could be extended with arrays of archive
type to generate on a specific platform:

[package.metadata.bundle]
osx_archives=["tar.gz"]
linux_archives=["tar.gz", "zip"]
windows_archives=["zip"]

For a package named foo version 0.1.0 on 64-bit Linux platform the output file could be:

foo_0.1.0_amd64.tar.gz

and could be generated aside other bundles (DEB, MSI etc).

The internall structure of the archive could follow the same structure that
OS specific bundles have at the moment, e.g. for Linux, same as
DEB packages have with bin, lib, share) directories etc.

Non-zero exit status doesn't pass through.

Looks like if the cargo build (or however it's called) fails, it doesn't cause cargo-bundle to exit with a non-zero exit status.

I changed the example to be non-buildable rust and did:

$ cargo bundle --example hello --target x86_64-apple-ios && echo $?
   Compiling cargo-bundle v0.4.0 (/Users/simlay/projects/cargo-bundle)
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `events_loop`
  --> examples/hello/main.rs:20:5
   |
18 |     aoeu
   |         - expected one of 8 possible tokens
19 |
20 |     events_loop.run(move |event, _, control_flow| {
   |     ^^^^^^^^^^^ unexpected token

warning: unused imports: `ControlFlow`, `Event`, `WindowEvent`
 --> examples/hello/main.rs:4:18
  |
4 |     event_loop::{ControlFlow, EventLoop}, event::{
  |                  ^^^^^^^^^^^
5 |         Event, WindowEvent
  |         ^^^^^  ^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

error: aborting due to previous error

error: could not compile `cargo-bundle`.

To learn more, run the command again with --verbose.
error: Result of `cargo build` operation was unsuccessful: exit code: 101
0

when it should probably be 101 or something.

[Windows] Error msi bundle

I get this error

error: Failed to collect resource file information
  Caused by: Le fichier spécifié est introuvable. (os error 2)

[iOS] Window size stuck at 320x480 due to lack of UILaunchStoryboard

I've been meaning to write this issue for a few weeks but can't seem to find the exact stack overflow page(s) that lead me to this issue so I'm doing this a bit from memory.

Anyway, I've been using winit as the main event loop and window creation tool for my projects and the main issue I've had consistently is that the iOS app screen resolution is 320x480 if I take the same app and bundle it with xcodegen, it doesn't have this issue. There are a number of stack over pages mentioning similar issue:

I've tested both cargo-bundle and the same code using xcodegen testing the size of of UIScreen::mainScreen().bounds() with uikit-sys.

Frankly, the Xcode release notes aren't super clear on what's being deprecated but what I've inferred is that the screen size was based on using an image per phone but now it's based on something mildly magic using the UILaunchStoryboard. If someone knows more about this I'm all ears.

Anyway, I just wanted to write this down for the next person who has this type of issue.

Issue installing a deb

When testing cargo-bundle to bundle and install the example project I run into the following error

Error while installing package: parsing file '/var/lib/dpkg/tmp.ci/control' near line 1

I'm using Eddy to manage my deb installs, it might be an issue on their end.

Respect the workspace.exclude Cargo.toml keys

This builds on #42

That implementation is a simple first implementation of detecting a folder/crate is part of a workspace.

But when it detects the "parent" Cargo.toml file with the workspace key, it should:

  • check if this crate is in the "workspace.include" key
  • check this crate is not in the "workspace.exclude" key and is hence explicitly excluded from the workspace.

We should double check if those keys can include wildcards or globs, as that would make it all a bit more complicated.

I'm not clear on what is correct if the parent has a simple "workspace" key, and this crate is defined as a dependency via a file path. Is it then part of the workspace? We can check where it's compiled into (which target directory) as that's basically what we are after to be able to package correctly.

Multiple separate packages

Hi! Just bounced in here, thanks for the application. Currently testing it out!

I wondered whether it would be difficult to have a project split up in different packages; for example, I have three executables in my src/bin, and most of the time, executable A doesn't make sense to be installed, and vice versa.

Automatic icon conversion

It would be useful if you could specify one icon file, and have it automatically converted to the appropriate format for the platform you're bundling for (e.g. .icns for Mac, .ico for windows, etc.), as well as adding support for hidipi displays

Support skipping build step

I am cross-compiling a program on Linux to MacOS and my build process cannot use build.rs as it needs to set environment variables used by transitive dependencies to configure their builds.

Because cargo-bundle always tries to build this makes things awkward. Also, I would like to run strip on the generated executables before bundling too which I am not sure is possible right now.

Could we allow an environment variable to skip the build step, maybe CARGO_BUNDLE_SKIP_BUILD which when set would omit the call to build_project_if_unbuilt()?

Suggestion: use the cargo metadata table

I haven't checked when this was introduced, but currently it is possible to use Cargo.toml for storing external metadata info.

This would work by putting the following on Cargo.toml

[package.metadata.bundle]
name = "ExampleApplication"
identifier = "com.doe.exampleapplication"
icon = ["32x32.png", "128x128.png", "[email protected]"]
version = "1.0.0"
resources = ["assets", "configuration", "secrets/public_key.txt"]
copyright = "Copyright (c) Jane Doe 2016. All rights reserved."

When maintaining packages I always prefer to keep things close, and having them on a single file seems like a good idea.

What do you think?

Provide a cross-platform way to load bundled resources

Seems like one of the strengths of cargo-bundle is being able to easily handle resource files in a cross-platform way (something I've found to be a pain to deal with on previous, non-Rust projects). While it already provides an easy way to include arbitrary resource files in the bundle, that still leaves the other half of the job: finding and loading those resources once the application is running.

A few ideas for what this could look like:

  • cargo-bundle could provide a companion library (since a single crate can have both a binary and a library) that programs can depend on for loading resources. Since this library would share code with the cargo-bundle binary, they'd be easy to keep in sync.
  • The simplest API for this library that comes to mind would be function that takes a file path/URL as written in the resources field of the bundle spec, and returns a path to where that file was actually placed by the bundle, depending on what system you're running on (so on OSX it would use e.g. CFBundleCopyResourcesDirectoryURL, on Debian it would look in /usr/lib/package_name/, etc). That way your actual application code doesn't need to care about what OS it's running on.
  • Ideally, this library would also work correctly if you run your application via cargo run rather than bundling it first, in which case it would load the resources from their original location as written in the resources field of the bundle spec. (Although I'm not sure off the top of my head what is the best way for the library to detect this situation).

Thoughts?

Make cargo-build cargo workspace aware

Problem

At the moment cargo-build is not aware of cargo workspaces.

So, when it is used in a "member" project of a workspace the target directory is calculated incorrectly, and it does not find the files constructed (e.g. binary) by cargo prior to it running and they cannot be packaged.

Running cargo bundle currently in a member directory of such a project will lead to this output:

cargo bundle
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
    Bundling flowc.app
Error: the source path is not an existing regular file

Proposed Solution

Make cargo-build aware of workspaces.

When it runs, search for cargo workspaces in the parent directories (need to check if that is recursive and multiple levels or not), and parse that Cargo.toml to determine the correct target directory to us.

Need to check for any ways that the standard target directory can be overridden.
According to this issue (rust-lang/cargo#1657) there are at least two:

build.target-dir configuration key
(I'm not sure if that is in Cargo.toml, or any of the .cargo/config files that might apply!)

CARGO_TARGET_DIR=foo; cargo run args

I suspect I might not get all that 100% correct first time, but help fleshing out the correct spec and algorithm for determining what the target directory should be would be appreciated.

Add config field for app category

  • For OS X apps, there is a key in the Info.plist file called called LSApplicationCategoryType that specifies what kind of app it is (e.g. for listing in the app store); the valid values are listed here.
  • For Debian apps, there is a key in the .desktop file called Categories that specifies what kind of app it is; the valid values are listed here. Naturally, it is a similar-but-not-quite-the-same list of categories as for OS X apps.
  • RPM packages have the (deprecated) group tag, which categories packages, as listed here. And so on.

It would be nice to have a single category config field you could set, and have cargo-bundle automatically translate as needed for each OS. For example, if you specified category = "BoardGame", then cargo-bundle would set LSApplicationCategoryType to "public.app-category.board-games" for OS X.

Dependency specification

I know it is still work in progress (mostly because I had to use the code from master instead of the regular released version). I tried to create a .deb package, which went mostly well. However, it would be great if I could somehow specify the dependencies for the package (the deb package currently has none). Or did I miss it somewhere in the documentation?

Improve the workspace awareness by using child crate's "workspace" Cargo.toml key if present

I understand that a child crate in a workspace can include a "workspace" key with a path - that points directly to the workspace root folder (where the workspace's Cargo.toml file should reside).

This means the parent folder doesn't have to be a direct parent of the current crate, and could be in a "sibling" (or child, which would be weird) folder on the file system.

cargo-bundle on crates.io is 2 years out of date.

I updated cargo-bundle via cargo install --force cargo-bundle and found myself with an older version.

I've since updated via cargo install cargo-bundle --git https://github.com/burtonageo/cargo-bundle to get the version I actually wanted but figured I'd write a ticket for the next person who ends up going down a small rabbit hole and wonders why it's broken.

gtk-rs program does not receive all keyboard input

I am not sure what is actually the cause here, but I tried to bundle a simple gtk-rs program and after launching, normal keys do not work in a text box. Delete, arrow keys etc., however, work perfectly. This doesn't happen when the program is run from the command line.

Cargo.lock is too old to build cargo-bundle

I tried to build cargo-bundle and it failed (sorry I don't have the message handy), but someone pointed out to me that if you run cargo update and then cargo install things work out.

Please run cargo update and check in the new Cargo.lock and make a new release.

Thanks!

Make `cargo-bundle` to work with terminals which don't support colors

Currently cargo-bundle relies on term to determine a terminal (see print_bundling() function as example), thus when term::stdout() returns None, we will observe the following behavior:

...
    let mut output = match term::stdout() {
        Some(terminal) => terminal,
        None => bail!("Can't write to stdout"),
...

Because of this limitation, cargo-bundle will make things like CI/CD Jenkins fail when executing this place (in my case the builds have failed multiple times and I had to investigate to understand where the problem is).

An way to go would be to use std::io::stdout() when term::stdout() fails to get a terminal.

What do you think about that?

A quick fix which did a trick in my case was:

diff --git a/src/bundle/common.rs b/src/bundle/common.rs
index e5d67fe..22b0edf 100644
--- a/src/bundle/common.rs
+++ b/src/bundle/common.rs
@@ -1,6 +1,6 @@
 use std::ffi::OsStr;
 use std::fs::{self, File};
-use std::io::BufWriter;
+use std::io::{self, BufWriter, Write};
 use std::path::Path;
 use term;
 use walkdir::WalkDir;
@@ -52,16 +52,21 @@ pub fn copy_to_dir(from: &Path, to_dir: &Path) -> ::Result<()> {
 /// Prints a message to stdout, in the same format that `cargo` uses,
 /// indicating that we are creating a bundle with the given filename.
 pub fn print_bundling(filename: &str) -> ::Result<()> {
-    let mut output = match term::stdout() {
-        Some(terminal) => terminal,
-        None => bail!("Can't write to stdout"),
+    match term::stdout() {
+        Some(mut output) => {
+            output.attr(term::Attr::Bold)?;
+            output.fg(term::color::GREEN)?;
+            write!(output, "    Bundling")?;
+            output.reset()?;
+            write!(output, " {}\n", filename)?;
+            output.flush()?;
+        }
+        None => {
+            let mut stdout = io::stdout();
+            write!(stdout, "    Bundling")?;
+            write!(stdout, " {}\n", filename)?;
+        }
     };
-    output.attr(term::Attr::Bold)?;
-    output.fg(term::color::GREEN)?;
-    write!(output, "    Bundling")?;
-    output.reset()?;
-    write!(output, " {}\n", filename)?;
-    output.flush()?;
     Ok(())
 }

Automatically resize icon files to the next largest valid ICNS dimension

There's a comment in create_icns_file() in src/bundle/osx_bundle.rs:

        // TODO: If none of the icons are of usable sizes, we could also use
        // the image crate to scale icons to the appropriate sizes.

I ran into this when I supplied a 500x500 PNG and got a "No usable icon files found." error. Manually resizing it to 256x256 made it go away.

Workspace not correctly detected if workspace is also a project

#47 implemented awareness of parent workspaces when running cargo bundle. It seems not to work, though, when the parent workspace is also a project of its own - e.g. foo contains bar, like so:

foo
├── Cargo.toml
├── bar
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── src
    └── main.rs

The contents of foo/Cargo.toml are:

[package]
name = "foo"
version = "0.1.0"
authors = ["Søren Mortensen <[email protected]>"]

[dependencies]

[workspace]
members = [
    "bar",
]

I'm not entirely sure this is an intended usage of Cargo workspaces, but I had this setup in a project (until I changed it to work around this issue1). Running cargo bundle within bar/ results in the following error message:

error: Failed to copy binary from "/private/tmp/foo/bar/target/debug/bar"
Caused by: "/private/tmp/foo/bar/target/debug/bar" does not exist

It seems to be the case that the parent foo/target/ directory is not detected in the way it should be, and foo/bar/target/ is being used instead.

1Edit: to clarify, the workaround I used was to change the structure of the project so that it looks like so:

baz
├── bar
│   ├── Cargo.toml
│   └── src
│       └── main.rs
├── Cargo.toml
└── foo
    ├── Cargo.toml
    └── src
        └── main.rs

...where the contents of baz/Cargo.toml are:

[workspace]
members = [
    "foo",
    "bar",
]

[Windows] Support standalone executables

Hello, thanks for a great tool.

I'm using cargo bundle on macOS to successfully build an .app bundle.

On Windows, it errors when creating the msi bundle (this is already recorded as an issue).

However, my app doesn't really need to be an installer. It's just a simple executable and I was hoping to use cargo bundle to set the icon and metadata so that it can be run as an .exe file directly.

At the moment, I can do this with the winres crate. However, that requires a different set of config that has to be kept in-sync with cargo bundle's config. It also requires a small build script to set the icon and it doesn't support converting from a png, etc.

I was wondering whether it would be possible to roll this functionality into cargo bundle so it's just another --format it supports? That way, many of the people running into the msi problem could use this approach instead if their app is simple enough.

Thanks

Interest in install and launching bundled app for iOS?

I use cargo-bundle pretty regularly for testing out some rust integration with iOS frameworks on the iOS simulator. While building the bundle isn't all that difficult, I've come to realize that installing it on the simulator and running it can be annoying due to all the iOS specific crap there is.

For example, I'm currently playing around with iOS support for iced (the GUI library) and this is how I rebuild, install and launch the example:
cargo bundle --format ios --target x86_64-apple-ios && xcrun simctl install booted $PWD/../../target/x86_64-apple-ios/debug/bundle/ios/ios-example.app && xcrun simctl launch booted com.github.iced.simple

To describe what's going on, after the bundle is built xcrun simctl install booted $PATH_TO_BUNDLE installs the bundled iOS app on a booted simulator. One can also specify the ios simulator ID. The xcrun simctl launch booted $BUNDLE_ID launches the bundled iOS app. In my case, it's the bundled identifier used in the package.metadata.bundle from the Cargo.toml.

Anyway, if there's interest in accepting a PR adding something like --install and --launch as parameters to cargo-bundle, I'd be to do it.

[Windows] Error in creating msi bundle

Hi,

I encounter the following error

error: Failed to generate Component table
Caused by: "xxx-xxx-xxx.exe" is not a valid value for column "KeyPath"

Any idea?

Thanks,
Cem

Support to specify different target

I can build for a different architecture with cargo build --target (for example to build a statically linked binary, or to build a 32bit binary on 64bit system). It would be great if I could pass --target to cargo bundle as well and it could create a corresponding package. For example, the static binaries would help deploying the same .deb package on both ubuntu and debian systems of different versions in our company instead of having to build a different package for each of them.

Specifying supported mime types.

When a file is to be opened with a new program in the GNOME desktop, only those programs are considered that specify a compatible mime type in their .desktop files. EDIT: Only those programs are considered that specify an input file or url in the Exec field. (See #87)

Currently a program bundled with cargo-bundle cannot be associated to any file-type on a GNOME desktop (as far as I can tell).

Getting started: how to install cargo-bundle itself

It would be helpful to add a few lines to the Readme specifying that cargo-bundle can be added to the user's cargo installation using cargo install cargo-bundle. Took me a little bit of googling to find that, only discovered from the users.rust-lang.org page announcing cargo-apk.

I'll submit a PR to this effect.

cargo-bundle for macOS does not appear to inherit target name from Cargo.toml

Simple Cargo.toml file, looking something like this:

[package]
name = "my-project"
version = "0.1.0"
authors = ["Oluseyi Sonaiya <[email protected]>"]

and simple Bundle.toml, like this:

identifier = "domain.organization.my-project"
icon = "path/to/icon.png"
copyright = "(C) Organization, 2017"
short_description = "My Project for macOS"
long_description = "My Project lorme ipsum"

Consistently, running cargo bundle yields an app bundle named target/debug/.app. It's a valid app bundle, and I just rename it for convenience, but this is clearly a bug.

Btw, perhaps it explains #28.

Concept: what is bundle?

I've noticed some design inconsistency between platforms while implementing #12. As mentioned in #92, it makes sense to having some option to generate not an installer but just a bundle, but then we need some clarification for the term "bundle". I think current implementation is like below:

Platform What it's called Can be run directly?
Windows installer need to be installed by itself
macOS bundle can be run directly, portable but can be installed by user
Debian package need to be installed by package manager

I think installing stuff is a little out of scope of cargo bundle subcommand and our primary business is just wrapping up essential files needed to run the application. Installers and packages are just to place these files onto specific directory and/or register the app to the "Installed Applications" list of user's OS. Ofcourse it would be great if cargo becomes capable of such stuff by using cargo-bundle though, it's a secondary or subsequent step.

There may be multiple steps in bundling process:

  1. Build: already done by cargo build
  2. Collect: copy the executable and the required resources to a separate directory
  3. Sign (optional): codesign in platform-specific way, e.g. using rust-codesign
  4. Pack (optional): generate installer or package, and sign it too if 2 is enabled
  5. Deploy (optional but requires 2 and 3): upload to app store or similar, maybe out of scope

When a user runs cargo bundle, we just have to do upto 1. On macOS, this results in generating an *.app bundle, while others are just a bare directory containing an executable and resources. (But another concern is, we may have cargo bundle --bare option for macOS users who want just a bare directory)

Though, this is just a rough idea... Could someone advise?

cross compiling from linux to windows

Hey, Is it possible to create an msi installer file on my ubuntu laptop, I installed the toolchain and linker to set up cross compilation, and I can successfully create a .exe on my computer, and I was wondering if it was possible to cross compile and make a .msi from my linux laptop. I tried it, but it failed with:

warning: MSI bundle support is still experimental.
    Bundling cof_application.msi
error: Failed to collect resource file information
  Caused by: No such file or directory (os error 2)

Does anyone know if this is possible?

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.