Module: projects-protocol-internals Synopsis: Project manager protocol library 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 /// Useful constants define constant $project-file-format-version = 3; //---*** What should these really be? define constant = ; define constant = ; define constant = ; define constant = one-of(#"executable", #"dll"); /// Project properties define open abstract primary class () end class ; define open generic find-project (workspace :: , library-name :: ) => (project :: false-or()); define open generic open-project (workspace :: , locator :: , #key read-only? :: ) => (project :: ); define open generic project-name (project :: ) => (name :: false-or()); define open generic project-title (project :: ) => (name :: ); define open generic project-owners (project :: ) => (owners :: ); define open generic project-library-name (project :: ) => (name :: false-or()); define open generic project-location (project :: ) => (location :: false-or()); define open generic project-directory (project :: ) => (location :: false-or()); define open generic project-minor-version (project :: ) => (version :: ); define open generic project-minor-version-setter (version :: , project :: ) => (version :: ); define open generic project-major-version (project :: ) => (version :: ); define open generic project-major-version-setter (version :: , project :: ) => (version :: ); define open generic project-read-only? (project :: ) => (read-only? :: ); define open generic project-source-files (project :: ) => (files :: ); define open generic save-project (project :: , #key filename) => (); define open generic register-project-condition (project :: , condition :: ) => (); /// User settings define open abstract primary class () end class ; define open generic project-user-settings (project :: ) => (settings :: false-or()); /// Basic project define variable $default-library-major-version :: = 0; define variable $default-library-minor-version :: = 0; define open abstract primary class () constant slot project-workspace :: , required-init-keyword: workspace:; constant slot project-name :: , required-init-keyword: name:; constant slot project-owners :: = make(), init-keyword: owners:; constant slot project-file-location :: , required-init-keyword: file-location:; slot project-file-date :: , required-init-keyword: file-date:; /*---*** Can we avoid this? slot project-file-info :: , init-keyword: file-info:; */ slot project-library-name :: , required-init-keyword: library-name:; slot project-major-version :: = $default-library-major-version, init-keyword: major-version:; slot project-minor-version :: = $default-library-minor-version, required-init-keyword: minor-version:; slot project-source-files :: , required-init-keyword: source-files:; slot project-targets :: = make(); slot project-target :: , required-init-keyword: target:; slot project-user-settings :: false-or() = #f, init-keyword: user-settings:; slot project-can-be-closed? :: , required-init-keyword: can-be-closed?:; end class ; define method project-title (project :: ) => (title :: ) as-lowercase(as(, project.project-name)) end method project-title; define method project-directory (project :: ) => (directory :: ) project.project-location.locator-directory end method project-directory; /// Reading projects define method read-project-file (workspace :: , location :: , format == 2) => (project :: ) let file-date = file-property(location, #"write-date"); let (library-name, files, properties) = read-lid-data(location); let (major-version, minor-version) = lid-project-settings(location, properties); let files = concatenate(files, get-list-property(location, properties, #"c-files"), get-list-property(location, properties, #"c-source-files"), get-list-property(location, properties, #"c-header-files"), get-list-property(location, properties, #"c-libraries"), get-list-property(location, properties, #"rc-files"), get-list-property(location, properties, #"other-files")); let project = make-workspace-project (workspace, file-location: location, file-date: file-date, library-name: library-name, major-version: major-version, minor-version: minor-version, source-files: files); let target = make-lid-build-target(workspace, project, location, properties); add!(project.project-targets, target); project.project-target := target; project end method read-project-file; define method read-project-file (workspace :: , location :: , format == 3) => (project :: ) let file-date = file-property(location, #"write-date"); let (library-name, files, properties) = read-lid-data(location); let (major-version, minor-version) = lid-project-settings(location, properties); let project = make-workspace-project (workspace, file-location: location, file-date: file-date, library-name: library-name, major-version: major-version, minor-version: minor-version, source-files: files); let target = make-lid-build-target(workspace, project, location, properties); add!(project.project-targets, target); project.project-target := target; project end method read-project-file; /// Reading LID files define function read-lid-data (lid-location :: ) => (library-name :: , files :: , properties ::
) let (library-name, file-names, properties) = read-file-library-description(lid-location); let dylan-type = dylan-file-extension(); let project-directory = lid-location.locator-directory; let files = map-as(, method (name :: ) let filename = as(, name); let pathname = merge-locators(filename, project-directory); make(, directory: pathname.locator-directory, base: pathname.locator-base, extension: dylan-type) end, file-names); values(library-name, files, properties) end function read-lid-data; define function read-file-library-description (location :: ) => (library-name :: , file-names :: , properties ::
) let properties = read-file-library-description-internal(location); let library-name = get-symbol-property(location, properties, #"library"); let files-entry = get-list-property(location, properties, #"files"); unless (library-name) signal(make(, format-string: "The library description file %= does not contain " "the entry for library, which is mandatory.", format-arguments: vector(location))) end; values(library-name, files-entry | #[], properties) end function read-file-library-description; define function read-file-library-description-internal (location :: ) => (properties ::
) iterate do-it () block () let properties ::
= read-file-header(location); let lid-file = get-single-property(location, properties, #"lid"); local method merge-properties (p1 ::
, p2 ::
) => (merged-properties ::
) do(method (key) p2[key] := p1[key] end, key-sequence(p1)); p2 end method merge-properties; if (lid-file) let lid-locator = make(, directory: location.locator-directory, name: lid-file); let original-properties = read-file-library-description-internal(lid-locator); merge-properties(properties, original-properties) else properties end; exception (r :: ) do-it() end end end function read-file-library-description-internal; define function make-lid-build-target (workspace :: , project :: , location :: , properties ::
) => (target :: ) let loose-bindings = get-symbol-list-property (location, properties, #"loose-library-bindings"); let tight-bindings = get-symbol-list-property (location, properties, #"tight-library-bindings"); let compilation-mode = get-symbol-property(location, properties, #"compilation-mode") | *default-compilation-mode*; let target-type = get-symbol-property(location, properties, #"target-type"); let filename = get-symbol-property(location, properties, #"executable"); make-workspace-build-target (workspace, project: project, name: project.project-name, filename: filename, compilation-mode: compilation-mode, processor: workspace.workspace-processor, operating-system: workspace.workspace-operating-system, type: target-type, loose-bindings: loose-bindings, tight-bindings: tight-bindings) end function make-lid-build-target; define method lid-project-settings (location :: , properties ::
) => (major-version :: , minor-version :: ) let major-version :: = get-integer-property(location, properties, #"major-version") | $default-library-major-version; let minor-version :: = get-integer-property(location, properties, #"minor-version") | $default-library-minor-version; values(major-version, minor-version) end method lid-project-settings; /// Saving define method save-project (project :: , #key filename) => () if (project.project-read-only?) project-error(project, "Cannot save read-only project %s", project.project-title) end; let filename = filename | project.project-location; with-open-file (stream = filename, direction: #"output") write-project-file(stream, project, $project-file-format-version) end end method save-project; define method save-project-as-lid-file (project :: , #key filename) => () let filename = filename | project.project-location; with-open-file (stream = filename, direction: #"output") write-lid-file(stream, project) end end method save-project-as-lid-file; define method write-lid-file (stream :: , project :: ) => () write-lid-library-info(stream, project); write-lid-compiler-info(stream, project); write-lid-linker-info(stream, project); write-lid-debug-info(stream, project); end method write-lid-file; define function write-lid-library-info (stream :: , project :: , #key files? :: = #t) => () write-value(stream, #"library", project.project-library-name); if (files?) write-source-files (stream, project, #"files", type: #"dylan", extension?: #f) end; write-lid-library-version(stream, project) end function write-lid-library-info; define function write-lid-library-version (stream :: , project :: ) => () write-value(stream, #"major-version", project.project-major-version); write-value(stream, #"minor-version", project.project-minor-version); end function write-lid-library-version; define function write-lid-compiler-info (stream :: , project :: ) => () let target = project.project-target; let compilation-mode = target.target-compilation-mode; let loose-bindings = target.target-library-loose-bindings; write-value(stream, #"compilation-mode", compilation-mode); write-list-value(stream, #"loose-library-bindings", loose-bindings); end function write-lid-compiler-info; define function write-lid-linker-info (stream :: , project :: , #key base-address-string :: false-or() = #f) => () let target = project.project-target; write-value(stream, #"executable", target.target-filename); write-value(stream, #"target-type", target.target-type); write-value(stream, #"base-address", base-address-string | target.target-base-address-string); write-list-value(stream, #"linker-options", target.target-linker-options); write-source-files(stream, project, #"c-source-files", type: #"c"); write-source-files(stream, project, #"c-header-files", type: #"h"); write-source-files(stream, project, #"rc-files", type: #"rc"); write-source-files (stream, project, #"c-object-files", type: #"obj", relative?: #f); write-source-files (stream, project, #"c-libraries", type: #"lib", relative?: #f) end function write-lid-linker-info; define function write-lid-debug-info (stream :: , project :: ) => () let target = project.project-target; write-value(stream, #"debug-command", target.target-debug-command); write-value(stream, #"debug-arguments", target.target-debug-arguments); write-value(stream, #"debug-machine", target.target-debug-machine); write-value(stream, #"debug-directory", target.target-debug-directory); write-value(stream, #"start-function", target.target-start-function); end function write-lid-debug-info; define function write-source-files (stream :: , project :: , keyword :: , #key test :: = identity, type :: false-or() = #f, subprojects? :: = #f, relative? :: = #t, extension? :: = #t) => () let files = project-source-files-of-type (project, test: test, type: type, subprojects?: subprojects?); let project-directory = project.project-directory; unless (empty?(files)) write-keyword(stream, keyword); for (source-file :: in files) let pathname = case relative? => source-file; otherwise => relative-locator(source-file, project-directory); end; write-list-item (stream, case extension? => pathname; otherwise => make(, directory: pathname.locator-directory, base: pathname.locator-base); end) end end end function write-source-files; define function project-source-files-of-type (project :: , #key test :: = identity, type :: false-or() = #f, subprojects? :: = #f) => (source-files :: ) let files :: = make(); local method add-project-files (project :: ) for (source-file :: in project.project-source-files) if (test(source-file) & (~type | source-file.locator-extension-type == type)) add-new!(files, source-file, test: \=) end end end method add-project-files; if (subprojects?) //---*** We need to be careful of the order here... let target = project.project-target; do-target-files (method (file :: ) let project = file.file-project; project & add-project-files(project) end, target, type: ) end; add-project-files(project); as(, files) end function project-source-files-of-type; define method write-value (stream :: , keyword :: , value) => () value & format(stream, "%s:\t%s\n", keyword, value) end method write-value; define method write-list-value (stream :: , keyword :: , values :: ) => () unless (empty?(values)) write-keyword(stream, keyword); do(curry(write-list-item, stream), values) end end method write-list-value; define function write-comment (stream, comment :: ) write-value(stream, #"Comment", comment) end function write-comment; define method write-keyword (stream :: , keyword :: ) => () format(stream, "%s:", keyword) end method write-keyword; define method write-list-item (stream :: , value :: ) => () format(stream, "\t%s\n", value) end method write-list-item; define method write-list-item (stream :: , value :: ) => () write-list-item(stream, as(, value)) end method write-list-item; define method write-project-file (stream :: , project :: , format == 2) => () write-comment(stream, "This file is generated, please don't edit"); write-value(stream, #"format-version", format); write-value(stream, #"library", project.project-library-name); write-source-files(stream, project, #"files", type: #"dylan"); write-lid-library-version(stream, project); write-lid-compiler-info(stream, project); write-lid-linker-info(stream, project); write-lid-debug-info(stream, project); write-source-files(stream, project, #"other-files", test: method (locator :: ) let type = locator.locator-extension-type; select (type) #"dylan", #"project", #"lib", #"ico", #"bmp", #"c", #"h", #"rc" => #t; otherwise => #f; end end) end method write-project-file; define method write-project-file (stream :: , project :: , format == 3) => () write-comment(stream, "This file is generated, please don't edit"); write-value(stream, #"format-version", format); write-value(stream, #"library", project.project-library-name); write-source-files(stream, project, #"files"); write-lid-library-version(stream, project); write-lid-compiler-info(stream, project); write-lid-linker-info(stream, project); write-lid-debug-info(stream, project) end method write-project-file;