Pull GitHub repository using Bazel - github

I need to download an entire GitHub repository using Bazel. Since I'm quite new to this tool I'm not really sure how to achieve that.
My main idea is this:
write a custom repository rule in downloadgithubrepo.bzl (which is located in the project root just like the WORKSPACE file) such as:
def _impl(repository_ctx):
repository_ctx.download("url_to_zipped_github_repo", output='relative_path_to_output_file')
github = repository_rule(
implementation = _impl
and in the WORKSPACE file to write something like this:
load("//:downloadgithubrepo.bzl", "github")
and to invoke a build a BUILD file is needed (also located at the project root)
its contents are the following:
cc_library(
name = "testrun",
srcs = "main.c",
)
I had to add the main.c file otherwise the build is failing - that is one issue and the real issue is that this does not work, as in the build is passing but the GitHub repository is not downloaded.
Am I on the right path at all?? Has anyone done something like this before?

What you're looking might already be implemented in the new_git_repository repository rule, or the git_repository rule if the GitHub project already has Bazel BUILD files wired in.
If the GitHub project does not have BUILD files, a BUILD file is required when using new_git_repository. For example, if you want to depend on a file target (e.g. /foo/bar.txt) or rule target (e.g. a cc_library) in https://github.com/example/repository, and the repository does not have BUILD files, write these lines in your project's WORKSPACE file:
new_git_repository(
name = "example_repository",
remote = "https://github.com/example/repository.git",
build_file_content = """
exports_files(["foo/bar.txt"])
# you can also create targets
cc_library(
name = "remote_cc_library",
srcs = ["..."],
hdrs = ["..."],
)
""",
)
In your BUILD file, reference the external repository's targets using the # prefix:
cc_library(
name = "testrun",
srcs = ["main.c"],
data = ["#example_repository//:foo/bar.txt"],
deps = ["#example_repository//:remote_cc_library"],
)
When you run bazel build //:testrun, Bazel will..
Analyze the dependencies of //:testrun, which include the file main.c and targets from the external repository #example_repository.
Look up the WORKSPACE file for an external repository named example_repository, and finds the new_git_repository declaration.
Perform a git clone on the remote attribute specified in the example_repository declaration.
Write a BUILD file containing the build_file_content string at the project root of the cloned repository.
Analyze the targets #example_repository//:foo/bar.txt and #example_repository//:remote_cc_library
Build the dependencies, and hands them to your //:testrun cc_library.
Build //:testrun.
If the GitHub project does have BUILD files, you do not need to provide an BUILD file. You can refer to the targets directly after specifying the WORKSPACE dependency with git_repository:
git_repository(
name = "example_repository",
remote = "https://github.com/example/repository.git",
)
For more information, check out Bazel's documentation on External Repositories.

Related

Flutter windows: can I use relative path to bundle `.dll` library in CMakeLists.txt?

I am building a flutter plugin which calls native functions from lib.dll file and everything works as expected in my computer.
But I use relative path to link that lib such as
E:/_Projects/mahesabu/client/packages/server/windows/lib.dll
Now I want to move the build process in CI/CD which I believe using relative path such as
./lib.dll would be very easy.
Of cource I am new to cmake configuration. And in one comment it is written
List of absolute paths to libraries that should be bundled with the plugin
I wonder how can I use relative path there, because if I try build fails. The following is CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
set(PROJECT_NAME "server")
project(${PROJECT_NAME} LANGUAGES CXX)
# This value is used when generating builds using this plugin, so it must
# not be changed
set(PLUGIN_NAME "server_plugin")
add_library(${PLUGIN_NAME} SHARED
"server_plugin.cpp"
)
apply_standard_settings(${PLUGIN_NAME})
set_target_properties(${PLUGIN_NAME} PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
target_include_directories(${PLUGIN_NAME} INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)
# List of absolute paths to libraries that should be bundled with the plugin
set(server_bundled_libraries
""
"E:/_Projects/mahesabu/client/packages/server/windows/lib.dll" #USE RELATIVE PATH HERE
PARENT_SCOPE
)
Any help will be appreciated.
Just use:
set(server_bundled_libraries "${CMAKE_CURRENT_SOURCE_DIR}/lib.dll" PARENT_SCOPE)
The CMAKE_CURRENT_SOURCE_DIR variable will expand to current source directory as tracked by add_subdirectory. This is usually, but not always, the directory in which the present CMakeLists.txt resides. Presumably, this is E:/_Projects/mahesabu/client/packages/server/windows on your computer (given your remark that you expect ./lib.dll to work), but will be somewhere else on CI or elsewhere.

Datanucleus enhancement with Bazel

I'm trying to migrate Maven project to Bazel and having troubles with Datanucleus enhancement.
After jar-file is build, Datanucleus looks inside it and does some byte-code manipulation to enhance persistable classes. The way to perform this in Bazel is by defining a rule that takes the *.jar output of java_library rule and creates a new enhanced version of the library.
The problem that I have is that for my rule I need datanucleus-core package from external libraries. When I try to access it from a genrule by $(location //third_party:datanucleus_core) it point to a jar which has no classes:
(genrule) cmd = "echo $(location //third_party:datanucleus_core)"
bazel-out/local-fastbuild/bin/third_party/liborg_datanucleus_datanucleus_core.jar
(genrule) cmd = "jar tf $(location //third_party:datanucleus_core)"
META-INF/
META-INF/MANIFEST.MF
The jar-file resolved by Bazel in genrule from $(location //third_party:datanucleus_core) contains only META-INF/MANIFEST.MF with the following content:
Manifest-Version: 1.0
Created-By: blaze
I tried to use java_binary rule that adds a correct datanucleus_core.jar into classpath, but Datanucleus enhances my libary in-place and fails to write its changes on disk (rewrite the rule's input file). Also java_binary rule is not supposed to be used for building.
So the question is what is the best way to enhance jar library in Bazel running Datanucleus utility, which is provided as a third-party dependency in Maven repository?
Bazel build label: 0.3.2-homebrew, OS: OS X El Capitan (10.11.6), java: 1.8.0_92
Update
Datanucleus dependency declaration:
# WORKSPACE
maven_jar(
name = "org_datanucleus_datanucleus_core",
artifact = "org.datanucleus:datanucleus-core:5.0.3",
)
# third_party/BUILD
java_library(
name = "org_datanucleus_datanucleus_core",
visibility = ["//visibility:public"],
exports = ["#org_datanucleus_datanucleus_core//jar"],
)
(in my question I shortened org_datanucleus_datanucleus_core to datanucleus_core)
As Neil Stockton mentioned, you cannot enhance classes in a jar. So, the basic strategy will be:
Create the jar.
Unjar the class files.
Run the enhancements.
Jar it back up.
Steps 2 & 3 have to be rolled into 4, as Bazel insists that you declare all inputs & outputs to a build rule (and you cannot know what .class files a .java file will generate, so Bazel always jars them up).
Create a datanucleus.bzl file to declare your enhancement rule in. It should look something like:
# Run datastore enhancements on the java_library named "jarname".
def enhance(jarname):
# src is the name of the jar file your java_library rule generates.
src = "lib" + jarname + ".jar"
native.genrule(
name = jarname + "-enhancement",
srcs = [
src,
"//third_party:datanucleus_core"
],
outs = [jarname + "-enhanced.jar"],
cmd = """
# Un-jar the .class files.
jar tf $(location {0})
# Run the enhance.
classes=""
for $$class in $$(find . -name *.class); do
java -cp {0}:$(location //third_party:datanucleus_core) $$class
classes="$$classes $$class"
done
# jar them back up.
jar cf $# $$classes""".format(src),
)
(I'm not too familiar with datastore so the cmd might need some modification, but it should be that general idea.)
Then, in your BUILD file, you'd do:
java_library(
name = "my-lib",
srcs = glob(["*.java"]),
deps = ["..."],
)
# import the rule you wrote.
load('//:datanucleus.bzl', 'enhance')
enhance("my-lib")
Now you can do:
bazel build //:my-lib-enhanced.jar
and use my-lib-enhanced.jar as a dependency in other java_ rules.
More info on .bzl files: https://bazel.build/versions/master/docs/skylark/concepts.html.
Edited to add more info on depending on a jar:
There are a couple of options to get a jar that contains the content of datanucleus. First, you don't need the layer of indirection: you can just say:
srcs = [
src,
"#datanucleus_core//jar"
],
This will give you the actual jar.
If, for some reason, you need the jar to be in third_party, you can modify third_party/BUILD to create a deploy jar, which is a java binary that bundles up all of its dependencies for deployment (since you're not actually going to using it as a binary, you can use whatever you want for the main class name):
java_binary(
name = "datanucleus-core",
main_class = "whatever",
runtime_deps = ["#org_datanucleus_datanucleus_core//jar"],
)
genrule(
name = "your-lib",
srcs = [":datanucleus-core_deploy.jar", ...],
)
The :datanucleus-core_deploy.jar is called an implicit target: it's only built if requested, but it can be generated from your java_binary declaration.

How to handle CMake ExternalProject when repo (googletest) has multiple libraries?

How can I add external projects in CMake when the project's repo isn't the root of the library I want to use, but in fact contains two directories which are each root directories of repos that I want to use in my project?
I'm working to set up a framework CMake project that uses Google Test and Mock for testing, however when I try to download the google test repo (https://github.com/google/googletest) with ExternalProject_Add, it complains on build that it can't find the source for the project. Well, that's because Google have merged googletest and googlemock into a single project, except it's now two projects.
Some of the repo's file structure:
googletest-master/
├──[...no CMakeFiles.txt exists here...]
├──googletest/
│ ├──src/
│ └──CMakeFiles.txt
└──googlemock/
├──src/
└──CMakeFiles.txt
When I do the following...
ExternalProject_Add(
gtest
GIT_REPOSITORY https://github.com/google/googletest.git
TIMEOUT 10
INSTALL_COMMAND ""
LOG_DOWNLOAD ON
LOG_CONFIGURE ON
LOG_BUILD ON
PREFIX "googletest-master"
)
...it downloads the actual repo to googletest-master/src/gtest because I'm prefixing the repo with "googletest-master" to keep it out of my main source code, and it assumes that I'm downloading a project that is only source and that source is in the root directory.
So I'd like to accomplish two things:
Download the repo into the googletest-master directory, exactly as it would be if I cloned the repo there, or downloaded the zip off GitHub and extracted it.
Build and include both googletest and googlemock in my CMake project
You need single download step, but two build steps. Different ExternalProject_add command calls cannot share steps, but you can arrange all these steps into different calls with appropriate dependencies between them:
# Single download(git clone)
ExternalProject_Add(
googletest-master
DOWNLOAD_DIR "googletest-master/src" # The only dir option which is required
GIT_REPOSITORY https://github.com/google/googletest.git
TIMEOUT 10
LOG_DOWNLOAD ON
# Disable all other steps
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
)
# Build gtest from existing sources
ExternalProject_Add(
gtest
DOWNLOAD_COMMAND "" # No download required
SOURCE_DIR "googletest-master/src/googletest" # Use specific source dir
PREFIX "googletest-master" # But use prefix for compute other dirs
INSTALL_COMMAND ""
LOG_CONFIGURE ON
LOG_BUILD ON
)
# gtest should be build after being downloaded
add_dependencies(gtest googletest-master)
# Build gmock from existing sources
ExternalProject_Add(
gmock
DOWNLOAD_COMMAND "" # No download required
SOURCE_DIR "googletest-master/src/googlemock" # Use specific source dir
PREFIX "googletest-master" # But use prefix for compute other dirs
INSTALL_COMMAND ""
LOG_CONFIGURE ON
LOG_BUILD ON
)
# gmock should be build after being downloaded
add_dependencies(gmock googletest-master)
I have a repository where I import gtest and gmock libraries using CMake, just as you want to, but using the old SVN source repository instead of GIT.
I think the key is getting sources only for gmock, since it includes gtest sources and then export both libraries.
The CMakeLists.txt looks like this:
cmake_minimum_required(VERSION 2.8)
include(ExternalProject)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(GMOCK_VERSION "1.7.0")
set(GMOCK_DIR "${CMAKE_CURRENT_BINARY_DIR}/gmock-${GMOCK_VERSION}")
ExternalProject_Add(project_gmock
SVN_REPOSITORY http://googlemock.googlecode.com/svn/tags/release-${GMOCK_VERSION}
PREFIX ${GMOCK_DIR}
CMAKE_ARGS -DCMAKE_C_COMPILER:PATH=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER:PATH=${CMAKE_CXX_COMPILER}
# Disable update step
UPDATE_COMMAND ""
# Disable install step
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(project_gmock source_dir)
ExternalProject_Get_Property(project_gmock binary_dir)
include_directories(${source_dir}/gtest/include)
add_library(gtest STATIC IMPORTED)
set_property(TARGET gtest PROPERTY IMPORTED_LOCATION ${binary_dir}/gtest/libgtest.a)
add_dependencies(gtest project_gmock)
add_library(gtest_main STATIC IMPORTED)
set_property(TARGET gtest_main PROPERTY IMPORTED_LOCATION ${binary_dir}/gtest/libgtest_main.a)
add_dependencies(gtest_main project_gmock)
include_directories(${source_dir}/include)
add_library(gmock STATIC IMPORTED)
set_property(TARGET gmock PROPERTY IMPORTED_LOCATION ${binary_dir}/libgmock.a)
add_dependencies(gmock project_gmock)
add_library(gmock_main STATIC IMPORTED)
set_property(TARGET gmock_main PROPERTY IMPORTED_LOCATION ${binary_dir}/libgmock_main.a)
add_dependencies(gmock_main project_gmock)
include_directories(${cpp_utest_SOURCE_DIR}/src)
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")
#add_executable(utests )
#target_link_libraries(utests gmock_main gmock gtest pthread)

Using CMake to index source files of an external library with Eclipse

I am using CMake to build a project with external libraries by using "Eclipse CDT4 - Unix Makefiles".
Importing in Eclipse leads to a working project, but only all header files and my implemented source files are recognized correctly by the index of Eclipse.
I would also like to navigate through the source files for one external library by using "ctrl+click". I don't know how to add the *.cpp files of that external library in my CMakeList.txt to get them recognized by the indexer without building the library.
You can mark the .cpp files as "header file only" like this:
# find all filenames in the lib path and gather them in $YOUR_LIB
FILE(GLOB YOUR_LIB path_to_library/*.?pp)
# create a seperate sourcegroup so it doesn't clutter up the rest of your code
SOURCE_GROUP(\\lib FILES ${YOUR_LIB})
# mark them as header-file only
SET_SOURCE_FILES_PROPERTIES(${YOUR_LIB} PROPERTIES HEADER_FILE_ONLY TRUE)
# add both your code and the lib-code to the project
ADD_EXECUTABLE(program ${YOUR_CODE} ${YOUR_LIB})
I found a way to attach external library source files to the Eclipse project that is compatible with CMake project generator.
It turns out that to indexing and "ctrl+click" navigation works correctly only when external library sources are direct descendants of the project source folder. Therefore the solution is following:
Scan external library folder for source files.
Create a child folder under project's source folder.
Symlink discovered sources inside the created folder.
I created a CMake function attachExternalSources that performs above steps:
function(attachExternalSources librarySourceLocation folderName)
# Create folder for Geant4 sources
file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/${folderName})
message(STATUS "Searching for C++ sources in \"${librarySourceLocation}\"...")
FILE(GLOB_RECURSE libSources
${librarySourceLocation}/*.c
${librarySourceLocation}/*.cpp
${librarySourceLocation}/*.cxx
${librarySourceLocation}/*.cc
)
message(STATUS "Symlinking sources into\n \"${CMAKE_SOURCE_DIR}/${folderName}\"\n Please wait...")
foreach(source ${libSources})
# Obtain source filename
get_filename_component(source_filename ${source} NAME)
# Create symlink unless it already exists
set(symlink "${CMAKE_SOURCE_DIR}/${folderName}/${source_filename}")
if(NOT EXISTS ${symlink})
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${source} ${symlink})
endif()
endforeach()
# Scan all the symlinks created under the project folder and disable their compilation
FILE(GLOB sources_symlinks ${CMAKE_SOURCE_DIR}/${folderName}/*)
SET_SOURCE_FILES_PROPERTIES(${sources_symlinks} PROPERTIES HEADER_FILE_ONLY TRUE)
endfunction()
The use of the function is following. Paste above function code in your CMakeLists.txt. Next, use it as follows:
attachExternalSources("path/to/external/library/sources" "library-sources")
First parameter is location of the external library source code. Second argument is the name of a folder inside your project that that will contain source symlinks.
P.S. I tested function with Eclipse 4.19 and CMake 3.20.5.

Copy sources files into target directory with SBT

I recently decided to use SBT to build an existing project.
In this project I have some .glsl files within the scala packages which I need to copy during the compilation phase.
The project is structured like this :
- myapp.opengl
- Shader.scala
- myapp.opengl.shaders
- vertex_shader.glsl
- fragment_shader.glsl
Is this file structure correct for SBT or do I need to put the .glsl files into an other directory. And do you know a clean way to copy these files into the target folder ?
I would prefer not putting these files into the resources directory since they are (non-compiled) sources files
Thanks
I would not recommend putting those files into src/main/scala as they do not belong there. If you want to keep them separate from your resource files, you can put them in a custom path, e.g. src/main/glsl and add the following lines to your project definition to have them copied into output directory:
val shaderSourcePath = "src"/"main"/"glsl"
// use shaderSourcePath as root path, so directory structure is
// correctly preserved (relative to the source path)
def shaderSources = (shaderSourcePath ##) ** "*.glsl"
override def mainResources = super.mainResources +++ shaderSources