@@ -139,9 +139,11 @@ class ProjectBuilder {
139139 async build ( {
140140 destPath, cleanDest = false ,
141141 includedDependencies = [ ] , excludedDependencies = [ ] ,
142- dependencyIncludes
142+ dependencyIncludes,
143+ cacheDir,
144+ watch,
143145 } ) {
144- if ( ! destPath ) {
146+ if ( ! destPath && ! watch ) {
145147 throw new Error ( `Missing parameter 'destPath'` ) ;
146148 }
147149 if ( dependencyIncludes ) {
@@ -177,12 +179,15 @@ class ProjectBuilder {
177179 }
178180 }
179181
180- const projectBuildContexts = await this . _createRequiredBuildContexts ( requestedProjects ) ;
182+ const projectBuildContexts = await this . _createRequiredBuildContexts ( requestedProjects , cacheDir ) ;
181183 const cleanupSigHooks = this . _registerCleanupSigHooks ( ) ;
182- const fsTarget = resourceFactory . createAdapter ( {
183- fsBasePath : destPath ,
184- virBasePath : "/"
185- } ) ;
184+ let fsTarget ;
185+ if ( destPath ) {
186+ fsTarget = resourceFactory . createAdapter ( {
187+ fsBasePath : destPath ,
188+ virBasePath : "/"
189+ } ) ;
190+ }
186191
187192 const queue = [ ] ;
188193 const alreadyBuilt = [ ] ;
@@ -196,7 +201,7 @@ class ProjectBuilder {
196201 // => This project needs to be built or, in case it has already
197202 // been built, it's build result needs to be written out (if requested)
198203 queue . push ( projectBuildContext ) ;
199- if ( ! projectBuildContext . requiresBuild ( ) ) {
204+ if ( ! await projectBuildContext . requiresBuild ( ) ) {
200205 alreadyBuilt . push ( projectName ) ;
201206 }
202207 }
@@ -220,8 +225,12 @@ class ProjectBuilder {
220225 let msg ;
221226 if ( alreadyBuilt . includes ( projectName ) ) {
222227 const buildMetadata = projectBuildContext . getBuildMetadata ( ) ;
223- const ts = new Date ( buildMetadata . timestamp ) . toUTCString ( ) ;
224- msg = `*> ${ projectName } /// already built at ${ ts } ` ;
228+ let buildAt = "" ;
229+ if ( buildMetadata ) {
230+ const ts = new Date ( buildMetadata . timestamp ) . toUTCString ( ) ;
231+ buildAt = ` at ${ ts } ` ;
232+ }
233+ msg = `*> ${ projectName } /// already built${ buildAt } ` ;
225234 } else {
226235 msg = `=> ${ projectName } ` ;
227236 }
@@ -231,24 +240,27 @@ class ProjectBuilder {
231240 }
232241 }
233242
234- if ( cleanDest ) {
243+ if ( destPath && cleanDest ) {
235244 this . #log. info ( `Cleaning target directory...` ) ;
236245 await rmrf ( destPath ) ;
237246 }
238247 const startTime = process . hrtime ( ) ;
239248 try {
240249 const pWrites = [ ] ;
241250 for ( const projectBuildContext of queue ) {
242- const projectName = projectBuildContext . getProject ( ) . getName ( ) ;
243- const projectType = projectBuildContext . getProject ( ) . getType ( ) ;
251+ const project = projectBuildContext . getProject ( ) ;
252+ const projectName = project . getName ( ) ;
253+ const projectType = project . getType ( ) ;
244254 this . #log. verbose ( `Processing project ${ projectName } ...` ) ;
245255
246256 // Only build projects that are not already build (i.e. provide a matching build manifest)
247257 if ( alreadyBuilt . includes ( projectName ) ) {
248258 this . #log. skipProjectBuild ( projectName , projectType ) ;
249259 } else {
250260 this . #log. startProjectBuild ( projectName , projectType ) ;
261+ project . newVersion ( ) ;
251262 await projectBuildContext . getTaskRunner ( ) . runTasks ( ) ;
263+ project . sealWorkspace ( ) ;
252264 this . #log. endProjectBuild ( projectName , projectType ) ;
253265 }
254266 if ( ! requestedProjects . includes ( projectName ) ) {
@@ -257,8 +269,15 @@ class ProjectBuilder {
257269 continue ;
258270 }
259271
260- this . #log. verbose ( `Writing out files...` ) ;
261- pWrites . push ( this . _writeResults ( projectBuildContext , fsTarget ) ) ;
272+ if ( fsTarget ) {
273+ this . #log. verbose ( `Writing out files...` ) ;
274+ pWrites . push ( this . _writeResults ( projectBuildContext , fsTarget ) ) ;
275+ }
276+
277+ if ( cacheDir && ! alreadyBuilt . includes ( projectName ) ) {
278+ this . #log. verbose ( `Serializing cache...` ) ;
279+ pWrites . push ( projectBuildContext . getBuildCache ( ) . serializeToDisk ( ) ) ;
280+ }
262281 }
263282 await Promise . all ( pWrites ) ;
264283 this . #log. info ( `Build succeeded in ${ this . _getElapsedTime ( startTime ) } ` ) ;
@@ -269,9 +288,91 @@ class ProjectBuilder {
269288 this . _deregisterCleanupSigHooks ( cleanupSigHooks ) ;
270289 await this . _executeCleanupTasks ( ) ;
271290 }
291+
292+ if ( watch ) {
293+ const relevantProjects = queue . map ( ( projectBuildContext ) => {
294+ return projectBuildContext . getProject ( ) ;
295+ } ) ;
296+ const watchHandler = this . _buildContext . initWatchHandler ( relevantProjects , async ( ) => {
297+ await this . #update( projectBuildContexts , requestedProjects , fsTarget , cacheDir ) ;
298+ } ) ;
299+ return watchHandler ;
300+
301+ // Register change handler
302+ // this._buildContext.onSourceFileChange(async (event) => {
303+ // await this.#update(projectBuildContexts, requestedProjects,
304+ // fsTarget,
305+ // targetWriterProject, targetWriterDependencies);
306+ // updateOnChange(event);
307+ // }, (err) => {
308+ // updateOnChange(err);
309+ // });
310+
311+ // // Start watching
312+ // for (const projectBuildContext of queue) {
313+ // await projectBuildContext.watchFileChanges();
314+ // }
315+ }
316+ }
317+
318+ async #update( projectBuildContexts , requestedProjects , fsTarget , cacheDir ) {
319+ const queue = [ ] ;
320+ await this . _graph . traverseDepthFirst ( async ( { project} ) => {
321+ const projectName = project . getName ( ) ;
322+ const projectBuildContext = projectBuildContexts . get ( projectName ) ;
323+ if ( projectBuildContext ) {
324+ // Build context exists
325+ // => This project needs to be built or, in case it has already
326+ // been built, it's build result needs to be written out (if requested)
327+ // if (await projectBuildContext.requiresBuild()) {
328+ queue . push ( projectBuildContext ) ;
329+ // }
330+ }
331+ } ) ;
332+
333+ this . #log. setProjects ( queue . map ( ( projectBuildContext ) => {
334+ return projectBuildContext . getProject ( ) . getName ( ) ;
335+ } ) ) ;
336+
337+ const pWrites = [ ] ;
338+ for ( const projectBuildContext of queue ) {
339+ const project = projectBuildContext . getProject ( ) ;
340+ const projectName = project . getName ( ) ;
341+ const projectType = project . getType ( ) ;
342+ this . #log. verbose ( `Updating project ${ projectName } ...` ) ;
343+
344+ if ( ! await projectBuildContext . requiresBuild ( ) ) {
345+ this . #log. skipProjectBuild ( projectName , projectType ) ;
346+ continue ;
347+ }
348+
349+ this . #log. startProjectBuild ( projectName , projectType ) ;
350+ project . newVersion ( ) ;
351+ await projectBuildContext . runTasks ( ) ;
352+ project . sealWorkspace ( ) ;
353+ this . #log. endProjectBuild ( projectName , projectType ) ;
354+ if ( ! requestedProjects . includes ( projectName ) ) {
355+ // Project has not been requested
356+ // => Its resources shall not be part of the build result
357+ continue ;
358+ }
359+
360+ if ( fsTarget ) {
361+ this . #log. verbose ( `Writing out files...` ) ;
362+ pWrites . push ( this . _writeResults ( projectBuildContext , fsTarget ) ) ;
363+ }
364+
365+ if ( cacheDir ) {
366+ this . #log. verbose ( `Updating cache...` ) ;
367+ // TODO: Only serialize if cache has changed
368+ // TODO: Serialize lazily, or based on memory pressure
369+ pWrites . push ( projectBuildContext . getBuildCache ( ) . serializeToDisk ( ) ) ;
370+ }
371+ }
372+ await Promise . all ( pWrites ) ;
272373 }
273374
274- async _createRequiredBuildContexts ( requestedProjects ) {
375+ async _createRequiredBuildContexts ( requestedProjects , cacheDir ) {
275376 const requiredProjects = new Set ( this . _graph . getProjectNames ( ) . filter ( ( projectName ) => {
276377 return requestedProjects . includes ( projectName ) ;
277378 } ) ) ;
@@ -280,13 +381,14 @@ class ProjectBuilder {
280381
281382 for ( const projectName of requiredProjects ) {
282383 this . #log. verbose ( `Creating build context for project ${ projectName } ...` ) ;
283- const projectBuildContext = this . _buildContext . createProjectContext ( {
284- project : this . _graph . getProject ( projectName )
384+ const projectBuildContext = await this . _buildContext . createProjectContext ( {
385+ project : this . _graph . getProject ( projectName ) ,
386+ cacheDir,
285387 } ) ;
286388
287389 projectBuildContexts . set ( projectName , projectBuildContext ) ;
288390
289- if ( projectBuildContext . requiresBuild ( ) ) {
391+ if ( await projectBuildContext . requiresBuild ( ) ) {
290392 const taskRunner = projectBuildContext . getTaskRunner ( ) ;
291393 const requiredDependencies = await taskRunner . getRequiredDependencies ( ) ;
292394
@@ -389,7 +491,9 @@ class ProjectBuilder {
389491 const {
390492 default : createBuildManifest
391493 } = await import ( "./helpers/createBuildManifest.js" ) ;
392- const metadata = await createBuildManifest ( project , buildConfig , this . _buildContext . getTaskRepository ( ) ) ;
494+ const metadata = await createBuildManifest (
495+ project , this . _graph , buildConfig , this . _buildContext . getTaskRepository ( ) ,
496+ projectBuildContext . getBuildCache ( ) ) ;
393497 await target . write ( resourceFactory . createResource ( {
394498 path : `/.ui5/build-manifest.json` ,
395499 string : JSON . stringify ( metadata , null , "\t" )
0 commit comments