diff options
Diffstat (limited to 'lisp/cedet/ede/proj-comp.el')
-rw-r--r-- | lisp/cedet/ede/proj-comp.el | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/lisp/cedet/ede/proj-comp.el b/lisp/cedet/ede/proj-comp.el new file mode 100644 index 00000000000..4c94b18f8f6 --- /dev/null +++ b/lisp/cedet/ede/proj-comp.el @@ -0,0 +1,346 @@ +;;; ede/proj-comp.el --- EDE Generic Project compiler/rule driver + +;;; Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2009 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Keywords: project, make + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: +;; +;; This software handles the maintenance of compiler and rule definitions +;; for different object types. +;; +;; The `ede-compiler' class lets different types of project objects create +;; definitions of compilers that can be swapped in and out for compiling +;; source code. Users can also define new compiler types whenever they +;; some customized behavior. +;; +;; The `ede-makefile-rule' class lets users add customized rules into thier +;; objects, and also lets different compilers add chaining rules to their +;; behaviors. +;; +;; It is important that all new compiler types be registered once. That +;; way the chaining rules and variables are inserted into any given Makefile +;; only once. +;; +;; To insert many compiler elements, wrap them in `ede-compiler-begin-unique' +;; before calling their insert methods. +;; To write a method that inserts a variable or rule for a compiler +;; based object, wrap the body of your call in `ede-compiler-only-once' + +(require 'ede) ;source object +(require 'ede/autoconf-edit) + +;;; Types: +(defclass ede-compilation-program (eieio-instance-inheritor) + ((name :initarg :name + :type string + :custom string + :documentation "Name of this type of compiler.") + (variables :initarg :variables + :type list + :custom (repeat (cons (string :tag "Variable") + (string :tag "Value"))) + :documentation + "Variables needed in the Makefile for this compiler. +An assoc list where each element is (VARNAME . VALUE) where VARNAME +is a string, and VALUE is either a string, or a list of strings. +For example, GCC would define CC=gcc, and emacs would define EMACS=emacs.") + (sourcetype :initarg :sourcetype + :type list ;; of symbols + :documentation + "A list of `ede-sourcecode' objects this class will handle. +This is used to match target objects with the compilers and linkers +they can use, and which files this object is interested in." + :accessor ede-object-sourcecode) + (rules :initarg :rules + :initform nil + :type list + :custom (repeat (object :objecttype ede-makefile-rule)) + :documentation + "Auxiliary rules needed for this compiler to run. +For example, yacc/lex files need additional chain rules, or inferences.") + (commands :initarg :commands + :type list + :custom (repeat string) + :documentation + "The commands used to execute this compiler. +The object which uses this compiler will place these commands after +it's rule definition.") + (autoconf :initarg :autoconf + :initform nil + :type list + :custom (repeat string) + :documentation + "Autoconf function to call if this type of compiler is used. +When a project is in Automake mode, this defines the autoconf function to +call to initialize automake to use this compiler. +For example, there may be multiple C compilers, but they all probably +use the same autoconf form.") + (objectextention :initarg :objectextention + :type string + :documentation + "A string which is the extention used for object files. +For example, C code uses .o on unix, and Emacs Lisp uses .elc.") + ) + "A program used to compile or link a program via a Makefile. +Contains everything needed to output code into a Makefile, or autoconf +file.") + +(defclass ede-compiler (ede-compilation-program) + ((makedepends :initarg :makedepends + :initform nil + :type boolean + :documentation + "Non-nil if this compiler can make dependencies.") + (uselinker :initarg :uselinker + :initform nil + :type boolean + :documentation + "Non-nil if this compiler creates code that can be linked. +This requires that the containing target also define a list of available +linkers that can be used.") + ) + "Definition for a compiler. +Different types of objects will provide different compilers for +different situations.") + +(defclass ede-linker (ede-compilation-program) + () + "Contains information needed to link many generated object files together.") + +(defclass ede-makefile-rule () + ((target :initarg :target + :initform "" + :type string + :custom string + :documentation "The target pattern. +A pattern of \"%.o\" is used for inference rules, and would match object files. +A target of \"foo.o\" explicitly matches the file foo.o.") + (dependencies :initarg :dependencies + :initform "" + :type string + :custom string + :documentation "Dependencies on this target. +A pattern of \"%.o\" would match a file of the same prefix as the target +if that target is also an inference rule pattern. +A dependency of \"foo.c\" explicitly lists foo.c as a dependency. +A variable such as $(name_SOURCES) will list all the source files +belonging to the target name.") + (rules :initarg :rules + :initform nil + :type list + :custom (repeat string) + :documentation "Scripts to execute. +These scripst will be executed in sh (Unless the SHELL variable is overriden). +Do not prefix with TAB. +Each individual element of this list can be either a string, or +a lambda function. (The custom element does not yet express that.") + (phony :initarg :phony + :initform nil + :type boolean + :custom boolean + :documentation "Is this a phony rule? +Adds this rule to a .PHONY list.")) + "A single rule for building some target.") + +;;; Code: +(defvar ede-compiler-list nil + "The master list of all EDE compilers.") + +(defvar ede-linker-list nil + "The master list of all EDE compilers.") + +(defvar ede-current-build-list nil + "List of EDE compilers that have already inserted parts of themselves. +This is used when creating a Makefile to prevend duplicate variables and +rules from being created.") + +(defmethod initialize-instance :AFTER ((this ede-compiler) &rest fields) + "Make sure that all ede compiler objects are cached in +`ede-compiler-list'." + (add-to-list 'ede-compiler-list this)) + +(defmethod initialize-instance :AFTER ((this ede-linker) &rest fields) + "Make sure that all ede compiler objects are cached in +`ede-linker-list'." + (add-to-list 'ede-linker-list this)) + +(defmacro ede-compiler-begin-unique (&rest body) + "Execute BODY, making sure that `ede-current-build-list' is maintained. +This will prevent rules from creating duplicate variables or rules." + `(let ((ede-current-build-list nil)) + ,@body)) + +(defmacro ede-compiler-only-once (object &rest body) + "Using OBJECT, execute BODY only once per Makefile generation." + `(if (not (member ,object ede-current-build-list)) + (progn + (add-to-list 'ede-current-build-list ,object) + ,@body))) + +(defmacro ede-linker-begin-unique (&rest body) + "Execute BODY, making sure that `ede-current-build-list' is maintained. +This will prevent rules from creating duplicate variables or rules." + `(let ((ede-current-build-list nil)) + ,@body)) + +(defmacro ede-linker-only-once (object &rest body) + "Using OBJECT, execute BODY only once per Makefile generation." + `(if (not (member ,object ede-current-build-list)) + (progn + (add-to-list 'ede-current-build-list ,object) + ,@body))) + +(add-hook 'edebug-setup-hook + (lambda () + (def-edebug-spec ede-compiler-begin-unique def-body) + (def-edebug-spec ede-compiler-only-once (form def-body)) + (def-edebug-spec ede-linker-begin-unique def-body) + (def-edebug-spec ede-linker-only-once (form def-body)) + (def-edebug-spec ede-pmake-insert-variable-shared (form def-body)) + )) + +;;; Querys +(defun ede-proj-find-compiler (compilers sourcetype) + "Return a compiler from the list COMPILERS that will compile SOURCETYPE." + (while (and compilers + (not (member sourcetype (oref (car compilers) sourcetype)))) + (setq compilers (cdr compilers))) + (car-safe compilers)) + +(defun ede-proj-find-linker (linkers sourcetype) + "Return a compiler from the list LINKERS to be used with SOURCETYPE." + (while (and linkers + (slot-boundp (car linkers) 'sourcetype) + (not (member sourcetype (oref (car linkers) sourcetype)))) + (setq linkers (cdr linkers))) + (car-safe linkers)) + +;;; Methods: +(defmethod ede-proj-tweak-autoconf ((this ede-compilation-program)) + "Tweak the configure file (current buffer) to accomodate THIS." + (mapcar + (lambda (obj) + (cond ((stringp obj) + (autoconf-insert-new-macro obj)) + ((consp obj) + (autoconf-insert-new-macro (car obj) (cdr obj))) + (t (error "Autoconf directives must be a string, or cons cell"))) + ) + (oref this autoconf))) + +(defmethod ede-proj-flush-autoconf ((this ede-compilation-program)) + "Flush the configure file (current buffer) to accomodate THIS." + nil) + +(defmethod ede-proj-makefile-insert-variables ((this ede-compilation-program)) + "Insert variables needed by the compiler THIS." + (if (eieio-instance-inheritor-slot-boundp this 'variables) + (with-slots (variables) this + (mapcar + (lambda (var) + (insert (car var) "=") + (let ((cd (cdr var))) + (if (listp cd) + (mapc (lambda (c) (insert " " c)) cd) + (insert cd))) + (insert "\n")) + variables)))) + +(defmethod ede-compiler-intermediate-objects-p ((this ede-compiler)) + "Return non-nil if THIS has intermediate object files. +If this compiler creates code that can be linked together, +then the object files created by the compiler are considered intermediate." + (oref this uselinker)) + +(defmethod ede-compiler-intermediate-object-variable ((this ede-compiler) + targetname) + "Return a string based on THIS representing a make object variable. +TARGETNAME is the name of the target that these objects belong to." + (concat targetname "_OBJ")) + +(defmethod ede-proj-makefile-insert-object-variables ((this ede-compiler) + targetname sourcefiles) + "Insert an OBJ variable to specify object code to be generated for THIS. +The name of the target is TARGETNAME as a string. SOURCEFILES is the list of +files to be objectified. +Not all compilers do this." + (if (ede-compiler-intermediate-objects-p this) + (progn + (insert (ede-compiler-intermediate-object-variable this targetname) + "=") + (let ((src (oref this sourcetype))) + (mapc (lambda (s) + (let ((ts src)) + (while (and ts (not (ede-want-file-source-p + (symbol-value (car ts)) s))) + (setq ts (cdr ts))) + ;; Only insert the object if the given file is a major + ;; source-code type. + (if ts;; a match as a source file. + (insert " " (file-name-sans-extension s) + (oref this objectextention))))) + sourcefiles) + (insert "\n"))))) + +(defmethod ede-proj-makefile-insert-rules ((this ede-compilation-program)) + "Insert rules needed for THIS compiler object." + (ede-compiler-only-once this + (mapc 'ede-proj-makefile-insert-rules (oref this rules)))) + +(defmethod ede-proj-makefile-insert-rules ((this ede-makefile-rule)) + "Insert rules needed for THIS rule object." + (if (oref this phony) (insert ".PHONY: (oref this target)\n")) + (insert (oref this target) ": " (oref this dependencies) "\n\t" + (mapconcat (lambda (c) c) (oref this rules) "\n\t") + "\n\n")) + +(defmethod ede-proj-makefile-insert-commands ((this ede-compilation-program)) + "Insert the commands needed to use compiler THIS. +The object creating makefile rules must call this method for the +compiler it decides to use after inserting in the rule." + (when (slot-boundp this 'commands) + (with-slots (commands) this + (mapc + (lambda (obj) (insert "\t" + (cond ((stringp obj) + obj) + ((and (listp obj) + (eq (car obj) 'lambda)) + (funcall obj)) + (t + (format "%S" obj))) + "\n")) + commands)) + (insert "\n"))) + +;;; Some details about our new macro +;; +(add-hook 'edebug-setup-hook + (lambda () + (def-edebug-spec ede-compiler-begin-unique def-body))) +(put 'ede-compiler-begin-unique 'lisp-indent-function 0) +(put 'ede-compiler-only-once 'lisp-indent-function 1) +(put 'ede-linker-begin-unique 'lisp-indent-function 0) +(put 'ede-linker-only-once 'lisp-indent-function 1) + +(provide 'ede/proj-comp) + +;;; ede/proj-comp.el ends here |