Should I use a dedicated bazel BUILD file for tests? - bazel

Is there any bazel convention for defining all of a project's test targets in some subfolder tests with its own BUILD file. Is that layout preferred to one main BUILD file that combines everything in one place: bins, libs, and tests?
An all in one build file could get lengthy, but it has the benefit of using in-package sibling target references, ie the ":siblingTarget" references.

Short answer: Yes.
Why would you like to couple your test with the implementation? You should always try to reduce dependencies between different parts of your code. That makes it easier to understand. See also here.
Imaging you find some BUILD file that contains implementation and test rules (e.g. cc_library, cc_binary, cc_test). Imagine some developer needed for some test some library - and only for testing proposes (e. g. cc_library(name="some_lib"). How would you know if this library is only needed for testing? If it is in a BUILD file contained in test folder then it would be clear from the folder structure that there is a high probability that this lib is only needed for testing proposes.

Related

Xcode: merge Unit tests into a single target

I'm developing a modular app with >30 modules, where each module has its own Unit test target. I also have a special test scheme which runs all Unit tests from all the targets.
The problem is - it's too slow. While the tests run fast, it takes a lot of time for Xcode to switch from one test target to another. The "all tests" scheme is supposed to be run in CI so I would really like to improve the performance.
I tried to convert all Unit test targets into static frameworks and link them to a new "merged" Unit test target. In this target I then created a single AllTests.swift file importing the frameworks and manually invoked tests on each of them. The performance improvement was ~4x.
However, this will be quite hard to maintain this file. My assumption is, since they are linked statically, there can be a way to make Xcode run imported in such a way tests automatically. If this works I'll be able to fully automate this process, keeping test targets for development and generating the "merged" target in CI.
What else I tried:
Investigated the option of using SourceKitten to parse the project and generate the AllTests.swift file automatically. This would require building the project one more time, eliminating the performance improvement.
Instead of linking frameworks, include references to source files into the merged target. This introduces a bunch of "multiple files with the same name" and "same declaration" errors that cannot be automatically resolved.
Made all declarations in the static frameworks public.
Googled a lot. This SO question is quite close but the solution doesn't work for me.
So the main question is - is there a way to make Xcode automatically include Unit tests that are part of a static framework the current target is linked against? Or any other suggestion on how I can automate this process will be very much appreciated.

Hundreds of targets, same code base in xcode configuration

We are at 400+ targets in xcode. It still works fine but there has to be a better way to set this up by keeping the same code base but not having all those targets which could slow down xcode.
Android Studio lets you update the appname, which loads that folder from disk so only that project is loaded to run and program against. In XCode that is not the case, all targets are available.
It's been years but is there a better way now, with hundreds of targets that doesnt involve Git or Branching? The questions in regards to this are old and only for a few projects, we are talking hundreds here.
Your question lacks enough context to make a specific recommendation but in general...
Use Frameworks
If you can, combine sensible things into a single (or multiple) framework target. Frameworks can be more than fancy wrappers around a dynamic library, they can contain helper tools and such as well.
Use Workspaces
If there is a logical grouping to your existing targets you can separate them out into their own Xcode projects. Once you have them in their own projects you can create a workspace that references those individual projects. Even if the combined workspace loads in everything upfront (I don't think it does tho) you can still open and use the separate projects for a fast and fluid experience when working on the components.
Use static libraries
If you have a ton of targets such that one requires A, B, and C, but another needs B, C, D then you can actually put A, B, C, and D together in a static library and rely on the linker to strip out unused code from each individual target. This obviously does not reduce the number of targets you have, but you can make the static library its own project and include it in a common workspace. This will also speed up compilation as the files only need be compiled once.
Parameterize Targets or Use Schemes
If your targets are simply wrapping some external build tool/script with hardcoded parameters (I've actually seen this) you can actually pass a ton of existing variables from xcode to these external tools and eliminate "duplicate" targets. Similarly you can add new schemes if some of your targets are simply permutations of each other. A good example I've seen of this are people that have a separate target for "sanitized" (address sanitizer, etc) builds you can instead create a sanitization scheme instead of a target.
Use "Script" Build Phases
If some of your targets are doing something such as linting then you can instead employ a script build phase to call the linter instead of having a separate target to do it.
Offload Targets to an External build System
Xcode can have targets that simply call out to an external tool/script using the Script build phase (and using variable parameters as mentioned above). This can make sense to do if you already use another build system (make, cmake, etc) for another platform. Use Xcode only for the Mac/iOS specific targets and offload everything else to a cross platform build system.
If the build system outputs errors in a format Xcode understands it will even show file and line errors the same as native Xcode targets. I've seen people write thin wrappers around external tools to catch parse and reprint errors into such a format.

Can BUILD files have arbitrary file extensions?

I am aware that bazel accepts both BUILD and BUILD.bazel as valid filenames.
The android tools seem to also have a BUILD.tools file.
In general, does bazel have any restrictions for a BUILD file's extension? For example, could i have BUILD.generated to delineate generate BUILD files from non-generated BUILD files?
The .tools extension is part of building Bazel itself. From the perspective of Bazel, it's just any ordinary file. It gets picked up here: https://github.com/bazelbuild/bazel/blob/bbc8ed16aee07c3ba9321d58aa4c0ffc55fa2ba9/tools/android/BUILD#L197
then eventually gets processed here: https://github.com/bazelbuild/bazel/blob/c816b89a2224c3c318f1228755ef41c53975f45c/src/create_embedded_tools.py#L74
For the use case you mention, one way to go about it is to generate a .bzl file with a meaningful name that contains a macro that you can call from a BUILD or BUILD.bazel file. That way you can separate the generated rules from manually maintained rules. This is similar to how generate_workspace works: https://docs.bazel.build/versions/master/generate-workspace.html

Import tests from one Unit test target into another

We started to split our swift iOS app into multiple frameworks to speed up our TDD feedback cycle. Each of these framework is a project with it's own unit test target. All of these are part of a larger workspace.
The issue is we can't run multiple test targets with our current CI setup. I was wondering if it's possible to bundle together all of our test targets into one for CI but keep them separated for development (maybe by importing/running tests from all the different targets into a "CI unit test target")?
You could certainly add another target and add REFERENCES to the files for the unit tests. They may more may not need modification of the module import names. A file may be referenced in multiple projects, and this could even be automated, as there are tools such as PBXProj for python to allow reading/writing of the Xcode project.
Alternatively, but I am not sure, it may be possible to create an aggregate target with the other items added - this is just speculation.
Perhaps a better question is why can't multiple test targets be run? Surely a script or fast lane could help?

Recommended strategy to accumulate data in bazel aspects output files

I'm writing a post-build tool that needs the location of a list of target's jar files.
For these locations I have an aspect that runs on a list of targets (separately for each target using --aspects) and fetch the jar file path for each of them.
I've managed to get each jar file path in a custom output file (e.g. jar.txt) in each target's output folder.
But this will mean I would need to go over each jar.txt file separately to get the location.
Is there a way to accumulate the jar files paths in a single file?
Something like:
Try and write to the same output folder with append command in the aspect. I'm not sure if a shared output folder is possible.
Create a synthetic target which depends on all the relevant targets, then run an aspect on this target and accumulate the jars and only write them at the root after the recursion is back.
Are 1. or 2. valid options?
What is the recommended strategy to accumulate data in bazel aspects output files?
Bazel doesn't provide facitlities in Skylark for accumulating information between targets that are not related to each other in the target graph (e.g. ones that are mentioned on the command line next to each other).
One possibility would be to write a Skylark rule that depends on all the targets you usually mention on the command line and built that one; that rule will be able to collate the classpaths from each Java target to a single file.
Another possibility is to tell Bazel to write build events (that includes all the outputs of all targets the specified build pattern expands to) to a file using the --experimental_build_event_{json,text,binary}_file. (The "experimental" will be removed soon.). The files contain instances of this message:
https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto
Natan,
If I understand correctly, you want to transitively propagate the information from each aspect node out into a single result. To do this, build the transitive set in your aspect rule implementation and pass it via the "provider" mechanism [^1]. I wrote up some examples on bazel aspects, perhaps you'll find it useful[^2].
https://github.com/pcj/bazel_aspects/blob/master/aspects.bzl#L94-L104
https://github.com/pcj/bazel_aspects

Resources