As differently configured executions might require different repository behaviours, it is necessary to have a repostiory interface which has the same lifetime as the execution itself. The BuildRepository interface serves the purpose of this.
Build repositories are responsible for holding data that can be configured using user defined build parameters. Based on these parameters the repositories can work in an execution specific way that is independent from other executions.
Build repositories can be created using their enclosing repositories, with a specific
environment configured for the current use-case. (See
SakerRepository.createBuildRepository(
The build repositories are responsible for looking up tasks that can be used during execution. The tasks are named using the TaskName class. The build repository can choose how they wish to interpret these names.
The lifecycle of a build repository depends on how the build system wishes to handle it. It can be longer than a single build execution, and they can even exist without even one build running with it.
The lifetime of a build repository is the following:
- Instantiation by SakerRepository.createBuildRepository(
RepositoryBuildEnvironment) - A build execution is started.
- The build repository is used during the execution.
- The build execution is finished.
- close() is called on the build repository.
Repositories can be cached to improve load performance for executions. When they are cached, detectChanges() will be called to detect any changes to the repository which would cause it to require reloading. See the method documentation for more information.
The build repositories can provide dynamic information about its contents for IDE usage. It is strongly recommended (but optional) that build repositories implement at least basic support for providing these informations via getScriptInformationProvider().
Build repositories should not access the SakerEnvironment caching functionality asynchronously. Meaning, that a build repository should not use cache keys and environment properties when they're not called during a build execution. Using it may lead unexpected linkage errors when closing the repositories.
public void | close() Closes this build repository. |
public default Object | Detects any changes in the underlying repository that occurred since the last execution finished. |
public default ExternalScriptInformationProvider | Gets the information provider for this repository that can be used to provide development assistance to the user. |
public default void | handleChanges( Handles the previously detected changes in detectChanges(). |
public TaskFactory< | lookupTask( Searches the corresponding task for the specified name. |
Calling this method signals that the build repository is not going to be used anymore. Implementations can release any resource related to this build repository, however they are not required to do so, and are allowed to keep resources cached in the enclosing SakerRepository parent. Closing of the parent repository should release those resources nonetheless.
Build repositories are required to unregister their installed class resolvers when they are closed. (See RepositoryBuildEnvironment.getClassLoaderResolverRegistry())
Calling this method again after the first call should be a no-op. I.e. this method should be idempotent.
Build repositories can be cached between executions to improve startup time for subsequent builds. Given the fact that repositories are shared objects, their underlying resources can be changed externally by external processes. In order to keep a valid state of cache between executions it is necessary to determine any changes that occurred before the next build execution.
Implementations of build repositories should detect any possible changes in the repository resources that can require reloading of cached resources. (E.g. classes, chached data, etc...)
Implementations of this method should take the possible file provider object changes described in RepositoryBuildEnvironment.getPathConfiguration() into account.
Example:
A repository implementation is backed by JARs to look up the tasks by name. A build repository is created, and an
execution is run with it. During this execution, the repository loads a task implementation from the JAR
task.jar. The build finishes successfully, and the build system caches this repository for future re-use.
During two consecutive executions, some external agents decide to modify this task.jar which contains the
task used by the execution.
As the next incremental build execution starts, this build repository will be re-used by the build system. It
will call detectChanges() to determine if any resources for the repository has changed. The
implementation should detect, that task.jar was modified externally, and return non-null
from
this method to signal the changes.
The cache will later call handleChanges(
If this method throws an exception, then all of the repository related cached data will be cleared/closed, and a completely new build repository instance will be created for the next execution.
The change detection is separated into two phases in order to avoid possible runtime errors after closing some
classpath related to repositories. After calling detectChanges(), the execution will clear cached data
in the build environment, and later call handleChanges(
It is possible that after detectChanges() returning non-null
, the
handleChanges(
Note: A repository implementation can decide that it doesn't implement change detection. This means that it will be harder for clients of the repository to implement plugins for it (if possible), and users will need to manually clear the build environment cache. This can result in slower incremental builds.
In general, changes in repositories should occur rarely, unless specific development is done. It can happen often if one is developing a plugin for a repository, but should happen rarely (preferably never) if one is just using the services provided by a repository.
null
if any changes were detected.
This method is only called if detectChanges() prevously returned non-null
.
Implementations should handle the previously detected changes. They can throw an appropriate RuntimeException to signal any errors during handling the changes. Throwing an exception from this method will cause the build execution to abort.
Looks up the task in this repository for the given name. An exception thrown with an explanation if the task cannot be found in this repository.
The lookup algorithm is implementation dependent for each repository.
Repository implementations are not required to return the same instances for the same name if this method is called consecutively.