Module: tools-interface Author: 1997-07-17: Seth LaForge Synopsis: Interface between spec file tools and the project manager. 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 // Overview of how tools work: // Tools are Dylan libraries which contain a top-level expression to // register themselves via tool-register. Tools are linked into the // environment and batch compiler by being "use"d in // D-environment-win32!*-library.dylan and D-app-dw!*-library.dylan. // // When a build is initiated, the project manager first iterates over all // files in the project, calling tool-name-from-specification() and // tool-find() for each to determine is a tool processes that file type. // For files for which tool-find() finds a tool: // The project manager calls the function returned, passing the // location of the file and the project file, and the date of the last // time the file was processed. This function reprocesses the file if // necessary, and returns a list of modified projects. // The project manager then rereads any project files which tools have // modified, and continues with the rest of the build. // // Any warnings or errors are reported by the tool by signaling // . The tool can also ask yes-no questions by // signalling . define variable tool-registry :: false-or() = #f; // Register a tool. Invokee should have signature: // (spec-file :: , project-file :: false-or(), // last-run :: false-or() // #key clean-build? :: = #f) // => (success? :: , // modified-projects :: /* of: */) // modified-projects is a of s of projects which have been // (or may have been) modified. // project-file should be #f only when the tool is invoked from the tool-tester // with one argument. // last-run is a date immediately after the previous run of the tool // on this specification file. // clean-build? indicates whether to force regeneration of all build products. // If success? is false, the build is aborted. // // Each tool should register itself via a top-level expression. define function tool-register (tool-name :: , invokee :: ) => () if (~ tool-registry) tool-registry := make(
); end if; tool-registry[tool-name] := invokee; end function tool-register; // Extract the tool name from a specification file. For .spec files, reads // the file to find a tool name - for others, simply returns the file // extension. // If there is an error, a nonrecoverable will // be signalled. define function tool-name-from-specification (spec-file :: ) => (tool-name :: ) // Get the file extension: let ext :: = spec-file.locator-extension | ""; let extsym = if (empty?(ext)) #"dylan" else as(, ext) end; //---*** andrewa: bootstrapping nastiness, only the first should be necessary if (extsym == #"spec" | extsym == #".spec") // Read the header of the file: let h = read-keyword-pair-file(spec-file); let tool = element(h, origin:, default: #()); if (tool.size ~= 1) tool-error("specification file must contain a single \"origin:\" " "declaration in its header", file: spec-file, serious?: #t); end if; as(, tool.first.keyword-file-element-value) else extsym end if end; // Find a tool invokee by name - used by project manager. // The name should have been returned by tool-name-from-specification. define function tool-find (tool-name :: ) => (invokee :: false-or()) if (tool-registry) element(tool-registry, tool-name, default: #f) end if; end function tool-find; // If the tool wishes to ask a yes-no question (for example, whether // to abort the translation when translation products have been // modified), it should signal this condition. The project manager // should catch the condition, present a yes/no dialog box, and return // #t/#f. define class () end class ; define function tool-ask-yes-no-question (format-string :: , #rest format-args) => (answer? :: ) signal(make(, format-string: format-string, format-arguments: format-args)); end function tool-ask-yes-no-question; // Warnings should be communicated to the project manager by // signalling the following condition. The project manager should // handle the condition, save the warning for future presentation to // the user somehow, and return. define class () // The message in the inherited format-string and format-arguments slots is // the body of the warning. slot tool-warning-serious? :: = #f, init-keyword: serious?:; slot tool-warning-recoverable? :: = #t, init-keyword: recoverable?:; slot tool-warning-file :: false-or(type-union(, )) = #f, init-keyword: file:; slot tool-warning-line :: false-or() = #f, init-keyword: line:; slot tool-warning-library :: false-or() = #f, init-keyword: library:; slot tool-warning-definition :: false-or() = #f, init-keyword: definition:; end class ; define function tool-warning (format-string :: , #rest keys) => () signal(apply(make, , format-string: format-string, keys)); end function tool-warning; define function tool-error (format-string :: , #rest keys) error(apply(make, , format-string: format-string, recoverable?: #f, keys)); end function tool-error; define method condition-to-string (this :: ) => (r :: ) format-to-string("%s: %s%s%s%s", if (this.tool-warning-recoverable?) if (this.tool-warning-serious?) "Serious warning" else "Warning" end if else "Error" end if, if (this.tool-warning-file) format-to-string("file %s%s: ", as(, this.tool-warning-file), if (this.tool-warning-line) concatenate(":", integer-to-string( this.tool-warning-line)) else "" end if) else "" end if, next-method(), if (this.tool-warning-definition) concatenate(" in ", this.tool-warning-definition) else "" end if, if (this.tool-warning-library) concatenate(" in library ", this.tool-warning-library) else "" end if) end method condition-to-string;