{"id":13450933,"url":"https://github.com/sayrer/bazel-lesson-1","last_synced_at":"2025-03-22T03:31:30.066Z","repository":{"id":141344299,"uuid":"183086606","full_name":"sayrer/bazel-lesson-1","owner":"sayrer","description":"Bazel Lesson 1","archived":false,"fork":false,"pushed_at":"2019-05-25T20:16:47.000Z","size":182,"stargazers_count":36,"open_issues_count":0,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-18T07:43:08.076Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sayrer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-04-23T19:55:58.000Z","updated_at":"2025-03-15T08:10:14.000Z","dependencies_parsed_at":"2023-05-04T08:01:40.680Z","dependency_job_id":null,"html_url":"https://github.com/sayrer/bazel-lesson-1","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sayrer%2Fbazel-lesson-1","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sayrer%2Fbazel-lesson-1/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sayrer%2Fbazel-lesson-1/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sayrer%2Fbazel-lesson-1/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sayrer","download_url":"https://codeload.github.com/sayrer/bazel-lesson-1/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244902929,"owners_count":20529114,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T07:00:40.490Z","updated_at":"2025-03-22T03:31:29.582Z","avatar_url":"https://github.com/sayrer.png","language":"Python","funding_links":[],"categories":["Resources"],"sub_categories":["Tutorials"],"readme":"# Bazel Lesson 1\n\nThis is a conversational introduction to Bazel. The first lesson will introduce basic Bazel concepts, and teach you how to build simple command line apps in Java and C++. It's recommended that you clone this repo and follow along in the terminal.\n\nThis text assumes you're running macOS 10.14, but it shouldn't be difficult to adapt the instructions for other operating systems. To get started, install [Bazel](https://docs.bazel.build/versions/master/install.html) and [Homebrew](https://brew.sh/), and make sure you have XCode installed.\n\n```\n$ bazel version\nBuild label: 0.24.1\nBuild target: bazel-out/darwin-opt/bin/src/main/java/com/google/devtools/build/lib/bazel/BazelServer_deploy.jar\nBuild time: Tue Apr 2 16:32:47 2019 (1554222767)\nBuild timestamp: 1554222767\nBuild timestamp as int: 1554222767\n\n$ brew --version\nHomebrew 2.1.1\nHomebrew/homebrew-core (git revision 11fb; last commit 2019-04-16)\nHomebrew/homebrew-cask (git revision 340e6; last commit 2019-04-16)\n```\n\nInstall graphviz, which we'll use to visualize our build.\n\n```\n$ brew install graphviz\n... (this will build a lot of stuff) ...\n$ dot -V\ndot - graphviz version 2.40.1 (20161225.0304)\n```\n\n# A simple C++ command line app\n\nAt the root of this repository, you'll see a file in all-caps called WORKSPACE. We won't get into its contents, but its purpose is to describe dependencies, both for our applications and extensions to Bazel itself.\n\nThe first thing we'll build is a trivial C++ command line application, with an associated library and a test. Bazel distributes what it calls \"targets\" throughout the project's directories. You specify targets using a path syntax, where \"//\" identifies the top-level directory that contains WORKSPACE. If you look in `cpp/BUILD`, you'll find three targets.\n\n```\n$ more cpp/BUILD \ncc_binary(\n    name = \"basic-app\",\n    srcs = [\"basic_app.cpp\"],\n    deps = [\n        \"basic\",\n    ]\n)\n\ncc_library(\n    name = \"basic\",\n    srcs = [\"basic_library.cpp\"],\n    hdrs = [\"basic_library.h\"],\n    visibility = [\"//visibility:public\"],\n)\n\ncc_test(\n    name = \"basic-test\",\n    srcs = [\"basic_library_test.cpp\"],\n    copts = [\"-Iexternal/gtest/include\"],\n    deps = [\n        \"@gtest//:main\",\n        \"basic\",\n    ],\n)\n```\n\nThe cc_binary target called \"basic-app\" is a command line binary you can run:\n\n```\n$ bazel run //cpp:basic-app\nINFO: Analysed target //cpp:basic-app (14 packages loaded, 128 targets configured).\nINFO: Found 1 target...\nTarget //cpp:basic-app up-to-date:\n  bazel-bin/cpp/basic-app\nINFO: Elapsed time: 2.688s, Critical Path: 0.62s\nINFO: 4 processes: 4 darwin-sandbox.\nINFO: Build completed successfully, 7 total actions\nINFO: Build completed successfully, 7 total actions\n\nI'm a C++ string!\n\n```\n\nWhat this does is create the binary, copy it into a working directory, and then run it. This ends up being really handy for packaging, because you can specify data files as dependencies and have them copied to the right place, without worrying about doing this yourself. Let's take a look at the dependencies of this tiny app with Bazel's query language.\n\n```\n$ bazel query \"deps(//cpp:basic-app)\"\n//cpp:basic-app\n//cpp:basic_app.cpp\n//cpp:basic\n@bazel_tools//tools/def_parser:def_parser\n@bazel_tools//tools/def_parser:no_op.bat\n@bazel_tools//tools/def_parser:def_parser_windows\n@bazel_tools//tools/def_parser:def_parser.exe\n@bazel_tools//third_party/def_parser:def_parser\n@bazel_tools//tools/cpp:malloc\n@bazel_tools//third_party/def_parser:def_parser_main.cc\n@bazel_tools//third_party/def_parser:def_parser_lib\n@bazel_tools//third_party/def_parser:def_parser.h\n@bazel_tools//third_party/def_parser:def_parser.cc\n@bazel_tools//src/conditions:remote\n@bazel_tools//src/conditions:host_windows\n@bazel_tools//tools/cpp:toolchain\n//external:cc_toolchain\n@local_config_cc//:toolchain\n...\n@local_config_cc//:libtool\n@local_config_cc//:cc_wrapper\n@local_config_cc//:cc_wrapper.sh\n@local_config_cc//:empty\n@bazel_tools//tools/objc:host_xcodes\n@bazel_tools//tools/cpp:link_dynamic_library\n@bazel_tools//tools/cpp:link_dynamic_library.sh\n@bazel_tools//tools/cpp:interface_library_builder\n@bazel_tools//tools/cpp:build_interface_so\n@bazel_tools//tools/cpp:grep-includes\n@bazel_tools//tools/cpp:grep-includes.sh\n//cpp:basic_library.h\n//cpp:basic_library.cpp\nLoading: 2 packages loaded\n```\n\nThis shows that our app depends on the files in our library, and the local C/C++ compiler (XCode). You can specify a specific compiler and toolchain in WORKSPACE for better reproducibility. If we add a few flags, we can narrow down the returned values to just our code.\n\n```\n$ bazel query  --nohost_deps --noimplicit_deps \"deps(//cpp:basic-app)\"\n//cpp:basic-app\n//cpp:basic_app.cpp\n//cpp:basic\n//cpp:basic_library.h\n//cpp:basic_library.cpp\n```\n\nAnd, we can visualize it using graphviz:\n\n`$ bazel query  --nohost_deps --noimplicit_deps \"deps(//cpp:basic-app)\" --output=graph | dot -Tpng | open -f -a /Applications/Preview.app`\n\n![alt text](./basic_cpp.png \"Bazel graph output\")\n\n\nIf you take a look at Bazel's output, you can see what's been built:\n\n```\n$ ls -l bazel-out/darwin-fastbuild/bin/cpp/\ntotal 64\ndrwxr-xr-x  4 sayrer  wheel    128 Apr 23 10:14 _objs\n-r-xr-xr-x  1 sayrer  wheel  23716 Apr 23 10:14 basic-app\ndrwxr-xr-x  4 sayrer  wheel    128 Apr 23 10:14 basic-app.runfiles\n-r-xr-xr-x  1 sayrer  wheel    150 Apr 23 10:14 basic-app.runfiles_manifest\n-r-xr-xr-x  1 sayrer  wheel   3008 Apr 23 10:14 libbasic.a\n```\n\nNext, we'll run our C++ test. The \"...\" at the end of the path tells bazel to run every test target under the //cpp/ path.\n\n```\n$ bazel test //cpp/...\n\nINFO: Elapsed time: 3.080s, Critical Path: 2.83s\nINFO: 14 processes: 14 darwin-sandbox.\nINFO: Build completed successfully, 18 total actions\n//cpp:basic-test                                                         PASSED in 0.1s\n\nExecuted 1 out of 1 test: 1 test passes.\nINFO: Build completed successfully, 18 total actions\n```\n\nIf you run it again, you'll note that it says \"(cached)\", meaning Bazel determined that the test needn't be rerun, as none of its dependencies had changed. If you edit one of the basic_library files, or the test file itself, Bazel will rerun the test. If you change basic_app.cpp (the file with the main function), it won't, because the test doesn't depend on the binary, only the library. Take another look at `cpp/BUILD` with this dependency graph in mind.\n\nThis caching feature applies to tests and builds, it will work in the presence of huge dependency graphs, and can be pushed very far with a caching build server. In companies where this is working really well, most files are already built for you, because someone has already done a build with a matching toolchain.\n\n# Java\n\nThe setup for Java is pretty similar, except that the convention is to follow Maven's directory structure. \n\n```\n$ bazel run //java/basic:command\nINFO: Analysed target //java/basic:command (21 packages loaded, 483 targets configured).\nINFO: Found 1 target...\nTarget //java/basic:command up-to-date:\n  bazel-bin/java/basic/command.jar\n  bazel-bin/java/basic/command\nINFO: Elapsed time: 9.680s, Critical Path: 5.86s\nINFO: 5 processes: 3 darwin-sandbox, 2 worker.\nINFO: Build completed successfully, 9 total actions\nINFO: Build completed successfully, 9 total actions\n\nHi from Java!\n\n```\n\nRunning the tests is a similar experience as well:\n\n```\n$ bazel test //java/...\nINFO: Analysed 3 targets (1 packages loaded, 6 targets configured).\nINFO: Found 2 targets and 1 test target...\nINFO: Elapsed time: 0.697s, Critical Path: 0.55s\nINFO: 1 process: 1 darwin-sandbox.\nINFO: Build completed successfully, 3 total actions\n//java/basic:test                                                        PASSED in 0.5s\n\nExecuted 1 out of 1 test: 1 test passes.\nINFO: Build completed successfully, 3 total actions\n\n\nsayrer:crossplatform sayrer$ bazel test //java/...\nINFO: Analysed 3 targets (1 packages loaded, 6 targets configured).\nINFO: Found 2 targets and 1 test target...\nINFO: Elapsed time: 0.156s, Critical Path: 0.01s\nINFO: 0 processes.\nINFO: Build completed successfully, 1 total action\n//java/basic:test                                               (cached) PASSED in 0.5s\n\nExecuted 0 out of 1 test: 1 test passes.\nINFO: Build completed successfully, 1 total action\n```\n\n# Combining C++ and Java\n\nTo wrap up this lesson, we'll combine our C++ and Java libraries in one executable using JNI.\n\n```\n$ bazel run -s //java/jni:command\nINFO: Analysed target //java/jni:command (0 packages loaded, 0 targets configured).\nINFO: Found 1 target...\nTarget //java/jni:command up-to-date:\n  bazel-bin/java/jni/command.jar\n  bazel-bin/java/jni/command\nINFO: Elapsed time: 0.127s, Critical Path: 0.00s\nINFO: 0 processes.\nINFO: Build completed successfully, 1 total action\nINFO: Build completed successfully, 1 total action\n\nHi from Java!\n\n\nI'm a C++ string!\n\n```\n\nHere, you can see our Java binary running the Java and C++ libraries we just built. To get a look at the dependency graph, run this command:\n\n`bazel query  --nohost_deps --noimplicit_deps \"deps(//java/jni:command)\" --output=graph | dot -Tpng | open -f -a /Applications/Preview.app`\n\nIf you go back and edit `cpp/basic_library.cpp`, you'll find that this target gets rebuilt as well.\n\n![alt text](./jni.png \"Bazel graph output\")\n\nThis dependency graph is starting to get large, but it's really not much compared to a production Bazel project.\n\n# Wrapping up\n\nLesson 1 should have explained the basic concepts behind Bazel, and shown why it is such a powerful system for building production software in multiple projects across a single company or organization. The next lesson will build on this basic skeleton to produce mobile clients, servers, and wire traffic they can use to communicate.\n\nCheck out [Bazel Lesson 2](https://github.com/sayrer/bazel-lesson-2) for more language integrations and some other advanced Bazel extensions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsayrer%2Fbazel-lesson-1","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsayrer%2Fbazel-lesson-1","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsayrer%2Fbazel-lesson-1/lists"}