Module: projects-protocol-internals Synopsis: Project build protocols Author: Andy Armstrong Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND /// Build target protocols define open abstract primary class () end class ; define macro target-protocols-definer { define target-protocols () ?settings:* end } => { ?settings } settings: { } => { } { setting ?name:name :: ?type:expression; ... } => { define open generic "target-" ## ?name (target :: ) => (?name :: ?type); define open generic "target-" ## ?name ## "-setter" (?name :: ?type, target :: ) => (?name :: ?type); ... } { constant setting ?name:name :: ?type:expression; ... } => { define open generic "target-" ## ?name (target :: ) => (?name :: ?type); ... } end macro target-protocols-definer; define open generic target-project (target :: ) => (project :: ); define open generic target-source-files (target :: ) => (files :: ); define open generic target-files (target :: ) => (files :: ); define open generic project-targets (project :: ) => (targets :: ); define open generic project-target (project :: ) => (target :: ); define open generic target-source-records (target :: ) => (records :: ); define open generic target-canonical-source-records (target :: ) => (records :: ); define open generic target-file-canonical-source-record (target :: , source :: ) => (record :: false-or(), modified? :: ); define target-protocols () setting processor :: ; setting operating-system :: ; setting compilation-mode :: ; setting copy-sources? :: ; setting read-only? :: ; setting build-directory :: false-or(); setting database-directory :: false-or(); setting profile-directory :: false-or(); setting bin-directory :: false-or(); setting release-directory :: false-or(); setting library-loose-bindings :: ; setting library-tight-bindings :: ; setting linker :: false-or(); setting filename :: false-or(); setting type :: ; setting linker-options :: false-or(); setting base-address-string :: false-or(); setting debug-command :: false-or(); setting debug-arguments :: false-or(); setting debug-machine :: false-or(); setting debug-directory :: false-or(); setting start-function :: false-or(); end target-protocols; define method target-workspace (target :: ) => (workspace :: ) target.target-project.project-workspace end method target-workspace; /// Build protocols define class () constant slot build-target :: , required-init-keyword: target:; constant slot build-compile? :: = #t, init-keyword: compile?:; constant slot build-link? :: = #t, init-keyword: link?:; constant slot build-release? :: = #t, init-keyword: release?:; constant slot build-subprojects? :: = #t, init-keyword: subprojects:; constant slot build-clean? :: = #f, init-keyword: clean?:; constant slot build-copy-sources? :: = *copy-canonical-sources?*, init-keyword: copy-sources?:; constant slot build-compilation-mode :: false-or() = #f, init-keyword: compilation-mode:; constant slot build-save? :: = #t, init-keyword: save?:; constant slot build-linker :: false-or() = #f, init-keyword: linker:; constant slot build-target-type :: false-or() = #f, init-keyword: target-type:; constant slot build-unify? :: = #t, init-keyword: unify?:; constant slot build-exports? :: = #t, init-keyword: exports?:; constant slot build-abort-reason :: = #"never", init-keyword: abort-reason:; constant slot build-debug-output :: = #[], init-keyword: debug-output:; constant slot build-progress-callback :: false-or() = #f, init-keyword: progress-callback:; end class ; define open generic open-project-database (project :: , #key target :: false-or()) => (); define open generic remove-target-build-products (target :: , #key subprojects? :: ) => (); define open generic targets-to-recompile (target :: ) => (targets :: ); define open generic targets-to-relink (target :: ) => (targets :: ); define open generic compile-target-library (target :: , build :: ) => (); define open generic generate-link-makefile (target :: , build :: ) => (); define open generic link-target-executable (target :: , build :: ) => (); define open generic note-workspace-build-operation-started (workspace :: , target :: ) => (); define open generic note-workspace-build-operation-finished (workspace :: ) => (); /// Build operations define function build-project (project :: , build :: ) => (success? :: ) let target = build.build-target; build-project-target(target, build) end function build-project; define method build-project-target (target :: , build :: ) => (success? :: ) let workspace = target.target-project.project-workspace; with-workspace-build (workspace, target) block (return) local method perform-build-operation (function :: , phase :: , start :: , finish :: ) note-build-phase-starting(target, phase, start, finish); function(target, build); note-build-phase-finished(target, phase, start, finish) end method perform-build-operation; perform-build-operation(prepare-targets, "Preparing", 0, 10); perform-build-operation(compile-targets, "Compiling", 10, 80); perform-build-operation(link-targets, "Linking", 80, 90); perform-build-operation(release-targets, "Releasing", 90, 100); #t exception () #f end end end method build-project-target; define function abort-build (project :: ) build-serious-warning(project, "Aborting compilation due to errors"); abort() end function abort-build; define method prepare-targets (target :: , build :: ) => () let targets = target.target-build-targets; let clean? = build.build-clean?; let total = targets.size; note-build-progress(target, 0, total); for (subtarget :: in targets, index :: from 0) build-message(target, "Updating sources for %s", target.target-title); refresh-target-files(subtarget, clean?: clean?); copy-source-files(subtarget, build); note-build-progress(target, index, total) end end method prepare-targets; define method compile-targets (target :: , build :: ) => () let targets = build-compile-targets(target, build); for (subtarget :: in targets, index :: from 1) compile-target-library(subtarget, build) end end method compile-targets; define method link-targets (target :: , build :: ) => () let targets = build-link-targets(target, build); for (subtarget :: in targets, index :: from 1) install-build-targets(subtarget, build); if (subtarget == target) link-target-executable(target, build) end end end method link-targets; define method release-targets (target :: , build :: ) => () if (build.build-release?) build-target-release(target) end end method release-targets; define method copy-source-files (target :: , build :: ) => () if (target.target-copy-sources?) install-build-files(target, ) end end method copy-source-files; define method build-compile-targets (target :: , build :: ) => (targets :: ) let compile? = build.build-compile?; let clean? = build.build-clean?; let subprojects? = build.build-subprojects?; let targets :: = case ~compile? => #[]; clean? => target.target-build-targets; otherwise => target.targets-to-recompile; end; if (~subprojects?) for (subtarget :: in targets) unless (subtarget == target) let project = subtarget.target-project; build-serious-warning (project, "Ignoring changes in %s", target.target-title) end end; if (member?(target, targets)) vector(target) else #[] end else targets end end method build-compile-targets; define method build-link-targets (target :: , build :: ) => (targets :: ) let link? = build.build-link?; let clean? = build.build-clean?; let subprojects? = build.build-subprojects?; let targets :: = case ~link? => #[]; clean? => target.target-build-targets; otherwise => target.targets-to-relink; end; if (~subprojects?) for (subtarget :: in targets) unless (subtarget == target) let project = subtarget.target-project; build-serious-warning (project, "Ignoring changes in %s", target.target-title) end end; if (member?(target, targets)) vector(target) else #[] end else targets end end method build-link-targets; define method install-build-targets (target :: , build :: ) => () let files = target.target-files; generate-link-makefile(target, build); install-build-files(target, ) end method install-build-targets; define method install-build-files (target :: , type :: subclass()) => () let directory = target.target-build-directory; do-target-files (method (file) install-file (target, file, directory, "Installing %s in the build directory") end, target, type: type) end method install-build-files; define method install-file (target :: , file :: , new-directory :: , message :: false-or()) => () let project = target.target-project; let directory = project.project-directory; let original = merge-locators(file.file-location, directory); let copy = make(, directory: new-directory, base: original.locator-base, extension: original.locator-extension); block() message & build-message(target, message, original); copy-file(original, copy, if-exists: #"replace") exception (error :: ) apply(build-serious-warning, project, error.condition-format-string, error.condition-format-arguments) end end method install-file; define method build-target-release (target :: ) => () error("Not yet implemented!") end method build-target-release; /// Build progress define function build-message (target :: , format-string :: , #rest format-arguments) => () apply(debug-message, format-string, format-arguments) end function build-message; define function note-build-phase-starting (target :: , phase :: , start :: , finish :: ) => () build-message(target, "Starting %s", phase) end function note-build-phase-starting; define function note-build-phase-finished (target :: , phase :: , start :: , finish :: ) => () build-message(target, "Finished %s", phase) end function note-build-phase-finished; define function note-build-progress (target :: , position :: , total :: ) => () build-message(target, "Progressing: %d/%d", position, total) end function note-build-progress; /// Build information define open abstract class () end class ; /// Recognized file extensions define class () end class ; define class () end class ; define class () sealed constant slot file-project :: false-or(), required-init-keyword: project:; end class ; define class () end class ; define class () end class ; //--- Copy all unrecognized files into the release directory. //--- Are there file types for which this is the wrong thing to do. define constant $default-file-information-class = ; define table $file-information-classes = { #"dylan" => , #"hdp" => , #"lid" => , #"ddb" => , #"c" => , #"h" => , #"rc" => , #"ico" => , #"bmp" => , #"lib" => , #"obj" => , #"dll" => , #"html" => }; define method read-file-information (target :: , extension :: , file :: ) => (information :: ) let class :: subclass() = element($file-information-classes, extension, default: $default-file-information-class); make(class, location: file) end method read-file-information;