Giter Club home page Giter Club logo

nix_bazel_codelab's Introduction

Nix+Bazel Codelab

This set of exercises is a practical introduction to building polyglot projects with Bazel, supported by Nix for reproducible, hermetic build environments. It is self-contained and works on Linux and macOS.

Please open an issue if you have questions or encounter problems running the examples.

Please make a pull request if you found and fixed an issue.

Background

This codelab is based on Google's Bazel Codelab. It has been adapted to

  • Use more recent versions of the required Bazel rule sets.
  • Follow best practices such as formatting with buildifier or using BUILD.bazel files instead of BUILD files.
  • Use Nix to provide Bazel and other developer tools in a Nix shell.
  • Use rules_nixpkgs to import Nix provided toolchains from nixpkgs.

Project Layout

  • direnv used to automatically set up shell environment, see .envrc.
  • Nix shell used to provide developer tools like Bazel itself, see shell.nix
  • Nix dependencies pinned under nix/.
  • Bazel layout
    • MODULE.bazel defines root of Bazel project and defines external module dependencies
      • Uses bazel_dep to import Bazel dependencies from the Bazel Central Registry (BCR)
      • Uses register_toolchains to register toolchains from nixpkgs
      • Uses use_repo to bring repositories into scope
      • Uses other rule sets' dedicated module extensions to import their dependencies, e.g. Go or NodeJS.
    • non_module_dependencies.bzl contains legacy WORKSPACE rules that are not available as native module extensions
      • Uses nixpkgs_*_configure to create toolchains
      • Can use http_archive to import Bazel dependencies
      • Can use nixpkgs_package to import Nix dependencies into Bazel
      • Can use other rule sets' dedicated repository rules to import their dependencies
    • .bazelrc configures Bazel
    • BUILD.bazel files define Bazel packages and targets in that package

Check your files against a known solution

The repository contains bazel build files that are known to work, they are shipped in the solutions subdirectory of the project.

A script named lab is added to your environment with nix-shell, it helps using the solutions files.

lab can be used in multiple ways:

  • lab help to get help and examples of use
  • lab compare $file to view a difference between your file and the solution if exists
  • lab display $file to view its solution if any
  • lab install $file to copy the solution if any (this will replace your work!)
  • lab install_all install all solutions file, the project should compile fine after running this command

Before you get started

Read up on Bazel Concepts and Terminology.

Install Nix and enter nix-shell

Install the Nix package manager with

sh <(curl -L https://nixos.org/nix/install) --daemon

All commands from the instructions are expected to run in the environment created by entering

nix-shell

If you want to use your favorite shell (e.g. fish) installed on your system:

nix-shell --run fish

Use direnv to automatically set up the environment when changing to this project's directory.

Getting started

This project contains missing pieces that you will have to write, with some guidance, to make it compile again.

In the files that require editing, you will find comments such as # section 1 code to add here helping you ensure that you are looking at the right place for the current exercise.

The lab script can be used if you need to compare your changes with the solution.

Feel free to open an issue to ask questions, or if you think something is wrong in the codelab.

Section 1: Hello, Bazel!

Build an example Java application that prints

Welcome to the Bootcamp! Let's get going :)

to the console.

  1. Edit java/src/main/java/bazel/bootcamp/BUILD.bazel
  2. Add a binary target for HelloBazelBootcamp.java
  3. Run the binary using bazel run //java/src/main/java/bazel/bootcamp:HelloBazelBootcamp
    • WARNING: The first time you run this command, it may take quite a long time. Bazel is downloading and installing all the dependencies required to build a Java project.

Section 2: Go server

Build a Go server that receives log messages in the format defined in proto/logger/logger.proto.

  1. Edit proto/logger/BUILD.bazel
    1. Add a target for the protocol description
    2. Add a library target for a Go protobuf library based on logger.proto
      • use go_proto_library()
        • name it logger_go_proto
        • importpath
          • allows importing the library from a known package path in server.go
        • compilers
          • setting to ["@rules_go//proto:go_grpc"] builds functions implementing the Logger service for gRPC
  2. Edit go/cmd/server/BUILD.bazel
    1. The go_library target server_lib is already written for you
    2. Add a binary target from server.go
      • use go_binary()
      • name it go-server
      • it should be compiled together with the sources of server_lib
  3. Run the go binary using bazel run //go/cmd/server:go-server
    • run implies the build step
  4. Check http://localhost:8081
    • it should display

      "No log messages received yet."

Section 3: Java client

Build a Java client which sends log messages to the server, in the format defined in proto/logger/logger.proto.

  1. Edit proto/logger/BUILD.bazel

    1. Add a Java library for data structures in logger.proto
    2. Add a Java library implementing the gRPC service Logger defined in logger.proto
  2. Edit java/src/main/java/bazel/bootcamp/BUILD.bazel

    1. Add a Java library implementing the logging client
      • use java_library()
      • name it JavaLoggingClientLibrary
      • use JavaLoggingClientLibrary.java as source
      • declare depencies on *_proto and *_grpc targets created in previous steps
      • since JavaLoggingClientLibrary uses the io.grpc package, you will also need to declare a dependency on @grpc-java//core, as well as a transport impementation such as @grpc-java//netty.
    2. Add a Java binary for the client
      • use java_binary()
      • name it JavaLoggingClient
      • use JavaLoggingClient.java as source
      • declare a dependency on JavaLoggingClientLibrary
  3. Run the Java binary with

    bazel run //java/src/main/java/bazel/bootcamp:JavaLoggingClient
    
    • workaround for an open nixpkgs issue on macOS
      env CC=gcc bazel run //java/src/main/java/bazel/bootcamp:JavaLoggingClient
      
      Note that it will not work by fixing CC to either clang or clang++, as both C and C++ dependencies are present!
  4. Run the Go binary from Section 2 from another nix-shell

  5. Type messages to send from the client to the server and view them on http://localhost:8081

Section 4: Java client unit tests

  1. Edit java/src/main/java/bazel/bootcamp/BUILD.bazel
    1. Add a test target for JavaLoggingClientLibrary
      • java_test()
      • name it JavaLoggingClientLibraryTest
        • names matter for tests!
        • target name, test class, and file name must be identical
      • use JavaLoggingClientLibraryTest.java as source
      • add JavaLoggingClientLibrary target from Section 3 as dependency
    2. Repeat the same for the client binary JavaLoggingClient
  2. Run tests
    • NOTE: this is not a unit test, it depends on the server to make any sense.
      • If you skipped section 2 exercise, you can simply run lab install go/cmd/server/BUILD.bazel to get the solution of this exercise and bazel run //go/cmd/server:go-server to run it.
    • start Go server in another nix-shell, as in Section 2
    • run Java client tests
      bazel test //java/src/main/java/bazel/bootcamp:JavaLoggingClientLibraryTest
      
      • NOTE: If you simply turn off the server and rerun the test, you will most likely see that the test passed, with the information that it is "cached". Indeed, Bazel does not rerun the tests if none of its dependencies changed. If you really want to rerun it to acknowledge that it fails when the server is off, you have to specify --cache_test_results=no in the command line.
    • Do not forget to run the tests of JavaLoggingClient
      • NOTE: Those tests do not use the server.

Section 5: Typescript web frontend

  1. Edit proto/logger/BUILD.bazel
    1. Add a TypeScript protobuf library implementing logger.proto
  2. Edit typescript/BUILD.bazel
    1. Add a target for the TypeScript app app.ts
      • use ts_project()
      • add logger_ts_proto as a dependency
      • set transpiler = "tsc"
      • you need to reference the local tsconfig.json
    2. Bundle the JavaScript modules using esbuild
      • use esbuild()
      • name it bundle
      • set a valid entrypoint (ts_project generates a file with the ending .js)
    3. Add a target for a development server
      • use js_run_devserver()
      • name it devserver
      • add the esbuild target and index.html as data dependencies
      • set args = ["typescript"] to serve files from the typescript directory
      • set tool = ":http_server" to make it work correctly
  3. Run the webserver using bazel run //typescript:devserver
  4. Run the Go server and Java client from the previous steps.
    • send messages from the Java client to the Go server
    • click the button on the web front end to see them in your web browser

Section 6: Integration test

  1. Edit tests/BUILD.bazel
    • add a shell test target for integrationtest.sh
    • use sh_test
    • add the Go server and Java client targets from previous steps as data dependencies
  2. Run the test using bazel test <target> and make sure that it passes
    • NOTE: You may have to modify your solution of Section 2 and 3 to make those tests pass. In particular, you most likely did not set the visibility of the binaries.
  3. Run the test multiple times using bazel test <target> --runs_per_test=10
    • QUESTION: Does it pass? If not, can you figure out why?
    • HINT: the section on test run behaviour in the tags documentation may prove useful.

Section 7: Code formatting with buildifier

  1. Edit BUILD.bazel
    1. Add a load instruction to import the rule buildifier.
      • NOTE: the documentation linked above assumes the use of the upstream buildifier rules. This project currently depends on a fork that ships precompiled binaries and is already available as a Bazel module.
    2. Create a target called buildifier-print which warns about lint error and only prints the changes buildifier would make.
      • NOTE: Adding such a rule will provide a warning that using mode = "diff" is deprecated, and suggesting to use a buildifier_test rule instead. There is a known issue preventing a buildifier_test from being run on every folders of a project, hence, for now, let's simply ignore this deprecation warning.
    3. Now create a target called buildifier-fix which fixes all the formatting errors (including linting ones).
    4. Run the 2 targets to format your solution to the exercises.

Section 8: Query dependency graph

  1. Read the Bazel Query How-To and try the examples to display the dependency graph of the packages in this exercise.
  2. SOLUTION: This exercise is solved here.

nix_bazel_codelab's People

Contributors

dkelmer avatar aherrmann avatar fricklerhandwerk avatar guillaumegen avatar malt3 avatar meteorcloudy avatar aiuto avatar laszlocsomor avatar avdv avatar rupertks avatar laurentlb avatar prednaz avatar tshaynik avatar dependabot[bot] avatar steshaw avatar matthew-healy avatar benradf avatar bryanhonof avatar dslomov avatar eytankidron avatar googleson78 avatar jeromeduval-nam avatar hicksjoseph avatar evertedsphere avatar meistert avatar vkleen avatar yannic avatar

Stargazers

Moritz Sanft avatar Zhe Ye avatar James Szalay avatar Joshua Bronson avatar Alexis DESTREZ avatar M. Amin Safavi avatar Zach Szafran avatar Eric McBride avatar Cameron Smith avatar Joe O'Pecko avatar Kenley Capps avatar Jan Weitz avatar name_snrl avatar Denis Silva avatar Loïc Roux avatar Ỵ͆̓̔̕̚̚͏͎̮͓̱͇̟̳u̝̻̪̱͖̜̙͠h̤̭̩͈͈͕̠ṳ̫̱̻i̦͓ H͢҈҃҃͠͡ avatar  avatar Garrett George avatar Will Mruzek avatar Alexander Hirner avatar Lam Tran avatar Ernad Husremović avatar  avatar AJ LaMarc avatar Rikito Taniguchi avatar Alexey Vakhrenev avatar  avatar Walter G. avatar Jonatan Borkowski avatar  avatar Oleg Lebedev avatar Rafed Ramzi avatar Siddhesh Bhupendra Kuakde avatar Jonan CM avatar Michel Vasconcelos avatar Isaac Torres avatar murtis avatar David Sanderson avatar Nasko Vasilev avatar Nick Korostelev avatar Sumit Mittal avatar  avatar  avatar  avatar

Watchers

Mathieu Boespflug avatar Chuck Grindel avatar Alexander Vershilov avatar Rajiv M Ranganath avatar Richard Bullington-McGuire avatar Yogesh Sajanikar avatar  avatar Nicolas Frisby avatar Mark Potter avatar  avatar  avatar Yves-Stan Le Cornec avatar Mark Karpov avatar Andy D avatar  avatar  avatar Vince avatar  avatar  avatar Mathieu Montin avatar  avatar  avatar

nix_bazel_codelab's Issues

Broken link in README as ts_proto_library has been removed from rules_nodejs

Describe the bug

The link on line 247 of the README to the ts_proto_library docs goes to a 404. This seems to be because this function has been removed from rules_nodejs, and so when we eventually update rules_nodejs, this will cause the codelab to break.

To Reproduce
Click the link.

Expected behavior
Link should go to documentation, and the codelab should be using an up to date target for building the Typescript protocol buffer library.
I can look into what current typescript protobuff Bazel best practices are and try to have a PR with a fix next week.

Environment

  • OS name + version: N/A
  • Version of the code: f0a0ea5

Is `go_dependencies()` necessary?

I tried removing the go_dependencies() invocation to play around with gazelle (because afaiu it's responsible for regenerating the deps.bzl, so I wanted to see what it does), and after running :gazelle-update-repos and :gazelle the go server runs fine, even without me adding back the invocation. Is there perhaps something else that's adding all the go repositories that are in deps.bzl?

Add support for darwin_arm64

Describe the bug

This codelab doesn't work on modern Macs; it fails with a toolchain error.

Here is what happens trying to complete Section 1: Hello, Bazel!:

nix_bazel_codelab on main
❯ bazel run //java/src/main/java/bazel/bootcamp:HelloBazelBootcamp  # 
ERROR: /private/var/tmp/_bazel_jab/3275b5a8772ff1ba5c3c27df62a9b077/external/local_config_cc/BUILD:48:19: in cc_toolchain_suite rule @local_config_cc//:toolchain: cc_toolchain_suite '@local_config_cc//:toolchain' does not contain a toolchain for cpu 'darwin_arm64'
...

Environment

  • OS name + version: macOS 14.3.1 (latest, released 2024-02-08)
  • Version of the code: latest (revision 9bfa6d2, dated 2024-02-12)

modernize the nix+bazel codelab

Additional context
there are many other dependencies to potentially update, some of which might be required and some of which might be low hanging fruit.

Tasks

  1. malt3
  2. 3 of 4
    type: feature request
  3. malt3

Replace working `WORKSPACE` and `BUILD.bazel` content with exercise placeholders

Is your feature request related to a problem? Please describe.
We had people report that reading through finished solutions does not make for any exercise, and starting from scratch is overwhelmingly hard.

Describe the solution you'd like
Adapt full solution to contain blanks, according to instructions, to be filled by learners.

Describe alternatives you've considered
This conflicts with #11, because having blanks would preclude actually building anything in CI. If at all, blanks should be produced by helper scripts.

Additional context
Migrated from to-do-list in README.

Running on NixOS requires manual intervention

The rules_proto repository uses pre-build binaries for all platforms which fails on NixOS because the location of the dynamic linker is non-standard.

Currently, on NixOS the WORKSPACE file has to be edited to apply a patch to rules_proto. Which is a bit cumbersome.

I think we should solve that more generally with a platform flag or something. This is probably not the last issue to encounter with NixOS. So yes, preferrably let us deal with it in a different context.

@fricklerhandwerk in #6 (comment)

Add exercise to create Go build instructions with Gazelle

Is your feature request related to a problem? Please describe.
Best practice would be not to manually collect Go dependencies, but Gazelle generate the rules.

Describe the solution you'd like
Write a tutorial section on how to do this.

Additional context
Migrated from to-do-list in README.

Update `./generate_workspace.sh` and `./generate_build_files.sh` to reflect the Nix based solution.

Is your feature request related to a problem? Please describe.
These seem to be scripts from Google's original codelab. The idea is to output correct solutions to the right files using the scripts. Currently they are not in sync with the builds set up in the actual files, which use rules_nixpkgs.

Describe the solution you'd like
Update the scripts to contain the current solution.

Describe alternatives you've considered

Additional context
Migrated from to-do-list in README.

Add an exercise to use buildifier

Buildifier is mentionned in the README which contains the statement of the exercises, but is used in none of the exercise.

I would add an exercise which ask the user to create 2 targets, one to list the problems buildifier identifies and a second one fixing them.

Test that the build succeeds in CI

Is your feature request related to a problem? Please describe.
Correctness of the solution is not validated automatically.

Describe the solution you'd like
Test all the builds in CI to make sure that modifications do not break the builds and confuse learners.

Additional context
Migrated from to-do-list in README.

Split `README.md` and `CONTRIBUTING.md`

Is your feature request related to a problem? Please describe.
Newcomers should be able to work through instructions linearly without getting confused by advanced details. Right now we have technical details that are relevant to contributors up front, which mention concepts which are irrelevant to actually learning the intended lessons.

Describe the solution you'd like
Add a CONTRIBUTING.md with an overview of the code base and existing hints at modifying it. Add a link in README.md.

Extract TODO items into GitHub issues.

Provide dedicated tutorial for the rules_nixpkgs setup

This is a Bazel tutorial, where Bazel is installed with Nix, but one could do exactly the same tutorial and install Bazel natively, it would work exactly the same.
The advantages of using Nix is hidden in the WORKSPACE file, where all the programming languages dependencies that we are dealing with are installed using Nix. But this is completely hidden to the reader of this tutorial. (And it is not a bug that it is hidden, I mean, there could be another tutorial focusing on it, but it should definitely not be mixed with this one).

Automatically detect NixOS platform

Is your feature request related to a problem? Please describe.
Running the codelab on NixOS requires a manual step.

Describe the solution you'd like
Provide a wrapper for http_archive that additionally takes a mapping of platforms to a list of patches. See _detect_host_platform() in rules_nixpkgs for an example, or is_supported_platform() for a much simpler one.

Another way to detect NixOS: https://discourse.nixos.org/t/detect-that-bash-script-is-running-under-nixos/11402

Describe alternatives you've considered
Keep the manual step. Whoever uses NixOS will be confident enough to uncomment two lines in WORKSPACE.

Start exercise with clean branch

"Generating" solutions with ./generate_build_files.sh and generate_workspace.sh requires keeping both the original and the generated code under version control, and precludes using tooling for editing the original. Instead put the solutions in the files they belong and at the very beginning add instructions to start from a clean state as such:

  1. Start from a clean workspace by running ./clean

The cleanup script should do what amounts to

rm WORKSPACE
fd BUILD.* -x rm {}
git checkout -b codelab
git commit -am "start codelab"

of course wrapped in nix-shell and with precautions to actually operate on the repository.

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.