saker.build Documentation TaskDoc JavaDoc Packages
public interface BuildRepository extends AutoCloseable
A build repository is a view to its enclosing SakerRepository configured for a specific build execution.

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(RepositoryBuildEnvironment))

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:

  1. Instantiation by SakerRepository.createBuildRepository(RepositoryBuildEnvironment)
  2. A build execution is started.
  3. The build repository is used during the execution.
  4. The build execution is finished.
  5. close() is called on the build repository.
The steps 2-4 can occur zero, one, or multiple times during its lifetime. The build repositories should be usable even when they are not being part of an execution, as IDE related features might be required to enhance user experience.

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.

Methods
public void
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(Object detectedchanges)
Handles the previously detected changes in detectChanges().
public TaskFactory<?>
Searches the corresponding task for the specified name.
public abstract void close() throws IOException
Closes this build repository.

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.

IOExceptionIn case of any error.
public default Object detectChanges()
Detects any changes in the underlying repository that occurred since the last execution finished.

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(Object) with the previously returned object to handle the detected changes. If the repository fails to handle the changes, then it should throw an appropriate RuntimeException. This exception will be reported to the user and the next execution will fail to start.

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(Object) in order to actually handle the detected changes. If the build system didn't separate the two phases, then clearing the cached data from the environment could result in NoClassDefFoundError or other related LinkageErrors when such code is invoked which has its classpath already closed.

It is possible that after detectChanges() returning non-null, the handleChanges(Object) won't be called. This might be due to other repositories throwing exceptions and prematurely aborting the execution. To avoid errors related to this, implementations shouldn't lock resources in this method.

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.

Non-null if any changes were detected.
Gets the information provider for this repository that can be used to provide development assistance to the user.
The script information provider.
public default void handleChanges(Object detectedchanges)
Handles the previously detected changes in detectChanges().

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.

detectedchangesThe previous return value from detectChanges().
public abstract TaskFactory<?> lookupTask(TaskName taskname) throws TaskNotFoundException
Searches the corresponding task for the specified name.

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.

tasknameThe name of the task to look up.
The found task for the given name.
TaskNotFoundExceptionIf the task was not found.