summaryrefslogtreecommitdiff
path: root/lisp/cedet/ede/generic.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/cedet/ede/generic.el')
-rw-r--r--lisp/cedet/ede/generic.el442
1 files changed, 442 insertions, 0 deletions
diff --git a/lisp/cedet/ede/generic.el b/lisp/cedet/ede/generic.el
new file mode 100644
index 00000000000..68517f27b6a
--- /dev/null
+++ b/lisp/cedet/ede/generic.el
@@ -0,0 +1,442 @@
+;;; ede/generic.el --- Base Support for generic build systems
+
+;; Copyright (C) 2010 Free Software Foundation, Inc.
+
+;; Author: Eric M. Ludlam <eric@siege-engine.com>
+
+;; 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:
+;;
+;; There are a lot of build systems out there, and EDE can't support
+;; them all fully. The ede-generic.el system is the base for
+;; supporting alternate build systems in a simple way, automatically.
+;;
+;; The structure is for the ede-generic baseclass, which is augmented
+;; by simple sub-classes that can be created by users on an as needed
+;; basis. The generic system will have targets for many language
+;; types, and create the targets on an as needed basis. All
+;; sub-project types will recycle the same generic target types.
+;;
+;; The generic target types will only be implemented for languages
+;; where having EDE support actually matters, with a single MISC to
+;; represent anything else.
+;;
+;; TOO MANY PROJECTS DETECTED:
+;;
+;; If enabling ede-generic support starts identifying too many
+;; projects, drop a file called `.ede-ignore' into any directory where
+;; you do not want a project to be.
+;;
+;; Customization:
+;;
+;; Since these projects are all so increadibly generic, a user will
+;; need to configure some aspects of the project by hand. In order to
+;; enable this without configuring the project objects directly (which
+;; are auto-generated) a special ede-generic-confg object is defined to
+;; hold the basics. Generic projects will identify and use these
+;; config files.
+;;
+;; Adding support for new projects:
+;;
+;; To add support to EDE Generic for new project types is very quick.
+;; See the end of this file for examples such as CMake and SCons.
+;;
+;; Support consists of one class for your project, specifying the file
+;; name used by the project system you want to support. It also
+;; should implement th method `ede-generic-setup-configuration' to
+;; prepopulate the configurable portion of the generic project with
+;; build details.
+;;
+;; Lastly, call `ede-generic-new-autoloader' to setup your project so
+;; EDE can use it.
+;;
+;; Adding support for new types of source code:
+;;
+;; Sources of different types are supported with a simple class which
+;; subclasses `ede-generic-target'. The slots `shortname' and
+;; `extension' should be given new initial values.
+;;
+;; Optionally, any target method used by EDE can then be overriden.
+;; The ede-generic-target-c-cpp has some example methods setting up
+;; the pre-processor map and system include path.
+;;
+;; NOTE: It is not necessary to modify ede-generic.el to add any of
+;; the above described support features.
+
+(require 'eieio-opt)
+(require 'ede)
+(require 'semantic/db)
+
+;;; Code:
+;;
+;; Start with the configuration system
+(defclass ede-generic-config (eieio-persistent)
+ ((extension :initform ".ede")
+ (file-header-line :initform ";; EDE Generic Project Configuration")
+ (project :initform nil
+ :documentation
+ "The project this config is bound to.")
+ ;; Generic customizations
+ (build-command :initarg :build-command
+ :initform "make -k"
+ :type string
+ :custom string
+ :group (default build)
+ :documentation
+ "Command used for building this project.")
+ (debug-command :initarg :debug-command
+ :initform "gdb "
+ :type string
+ :custom string
+ :group (default build)
+ :documentation
+ "Command used for debugging this project.")
+ ;; C target customixations
+ (c-include-path :initarg :c-include-path
+ :initform nil
+ :type list
+ :custom (repeat (string :tag "Path"))
+ :group c
+ :documentation
+ "The include path used by C/C++ projects.")
+ (c-preprocessor-table :initarg :c-preprocessor-table
+ :initform nil
+ :type list
+ :custom (repeat (cons (string :tag "Macro")
+ (string :tag "Value")))
+ :group c
+ :documentation
+ "Preprocessor Symbols for this project.")
+ (c-preprocessor-files :initarg :c-preprocessor-files
+ :initform nil
+ :type list
+ :custom (repeat (string :tag "Include File")))
+ )
+ "User Configuration object for a generic project.")
+
+(defun ede-generic-load (dir &optional rootproj)
+ "Return a Generic Project object if there is a match.
+Return nil if there isn't one.
+Argument DIR is the directory it is created for.
+ROOTPROJ is nil, since there is only one project."
+ ;; Doesn't already exist, so lets make one.
+ (let* ((alobj ede-constructing)
+ (this nil))
+ (when (not alobj) (error "Cannot load generic project without the autoload instance"))
+
+ (setq this
+ (funcall (oref alobj class-sym)
+ (symbol-name (oref alobj class-sym))
+ :name (file-name-nondirectory
+ (directory-file-name dir))
+ :version "1.0"
+ :directory (file-name-as-directory dir)
+ :file (expand-file-name (oref alobj :proj-file)) ))
+ (ede-add-project-to-global-list this)
+ ))
+
+;;; Base Classes for the system
+(defclass ede-generic-target (ede-target)
+ ((shortname :initform ""
+ :type string
+ :allocation :class
+ :documentation
+ "Something prepended to the target name.")
+ (extension :initform ""
+ :type string
+ :allocation :class
+ :documentation
+ "Regular expression representing the extension used for this target.
+subclasses of this base target will override the default value.")
+ )
+ "Baseclass for all targets belonging to the generic ede system."
+ :abstract t)
+
+(defclass ede-generic-project (ede-project)
+ ((buildfile :initform ""
+ :type string
+ :allocation :class
+ :documentation "The file name that identifies a project of this type.
+The class allocated value is replace by different sub classes.")
+ (config :initform nil
+ :type (or null ede-generic-config)
+ :documentation
+ "The configuration object for this project.")
+ )
+ "The baseclass for all generic EDE project types."
+ :abstract t)
+
+(defmethod initialize-instance ((this ede-generic-project)
+ &rest fields)
+ "Make sure the targets slot is bound."
+ (call-next-method)
+ (unless (slot-boundp this 'targets)
+ (oset this :targets nil))
+ )
+
+(defmethod ede-generic-get-configuration ((proj ede-generic-project))
+ "Return the configuration for the project PROJ."
+ (let ((config (oref proj config)))
+ (when (not config)
+ (let ((fname (expand-file-name "EDEConfig.el"
+ (oref proj :directory))))
+ (if (file-exists-p fname)
+ ;; Load in the configuration
+ (setq config (eieio-persistent-read fname))
+ ;; Create a new one.
+ (setq config (ede-generic-config
+ "Configuration"
+ :file fname))
+ ;; Set initial values based on project.
+ (ede-generic-setup-configuration proj config))
+ ;; Link things together.
+ (oset proj config config)
+ (oset config project proj)))
+ config))
+
+(defmethod ede-generic-setup-configuration ((proj ede-generic-project) config)
+ "Default configuration setup method."
+ nil)
+
+(defmethod ede-commit-project ((proj ede-generic-project))
+ "Commit any change to PROJ to its file."
+ (let ((config (ede-generic-get-configuration proj)))
+ (ede-commit config)))
+
+;;; A list of different targets
+(defclass ede-generic-target-c-cpp (ede-generic-target)
+ ((shortname :initform "C/C++")
+ (extension :initform "\\([ch]\\(pp\\|xx\\|\\+\\+\\)?\\|cc\\|hh\\|CC?\\)"))
+ "EDE Generic Project target for C and C++ code.
+All directories need at least one target.")
+
+(defclass ede-generic-target-el (ede-generic-target)
+ ((shortname :initform "ELisp")
+ (extension :initform "el"))
+ "EDE Generic Project target for Emacs Lisp code.
+All directories need at least one target.")
+
+(defclass ede-generic-target-fortran (ede-generic-target)
+ ((shortname :initform "Fortran")
+ (extension :initform "[fF]9[05]\\|[fF]\\|for"))
+ "EDE Generic Project target for Fortran code.
+All directories need at least one target.")
+
+(defclass ede-generic-target-texi (ede-generic-target)
+ ((shortname :initform "Texinfo")
+ (extension :initform "texi"))
+ "EDE Generic Project target for texinfo code.
+All directories need at least one target.")
+
+;; MISC must always be last since it will always match the file.
+(defclass ede-generic-target-misc (ede-generic-target)
+ ((shortname :initform "Misc")
+ (extension :initform ""))
+ "EDE Generic Project target for Misc files.
+All directories need at least one target.")
+
+;;; Automatic target aquisition.
+(defun ede-generic-find-matching-target (class dir targets)
+ "Find a target that is a CLASS and is in DIR in the list of TARGETS."
+ (let ((match nil))
+ (dolist (T targets)
+ (when (and (object-of-class-p T class)
+ (string= (oref T :path) dir))
+ (setq match T)
+ ))
+ match))
+
+(defmethod ede-find-target ((proj ede-generic-project) buffer)
+ "Find an EDE target in PROJ for BUFFER.
+If one doesn't exist, create a new one for this directory."
+ (let* ((ext (file-name-extension (buffer-file-name buffer)))
+ (classes (eieio-build-class-alist 'ede-generic-target t))
+ (cls nil)
+ (targets (oref proj targets))
+ (dir default-directory)
+ (ans nil)
+ )
+ ;; Pick a matching class type.
+ (when ext
+ (dolist (C classes)
+ (let* ((classsym (intern (car C)))
+ (extreg (oref classsym extension)))
+ (when (and (not (string= extreg ""))
+ (string-match (concat "^" extreg "$") ext))
+ (setq cls classsym)))))
+ (when (not cls) (setq cls 'ede-generic-target-misc))
+ ;; find a pre-existing matching target
+ (setq ans (ede-generic-find-matching-target cls dir targets))
+ ;; Create a new instance if there wasn't one
+ (when (not ans)
+ (setq ans (make-instance
+ cls
+ :name (oref cls shortname)
+ :path dir
+ :source nil))
+ (object-add-to-list proj :targets ans)
+ )
+ ans))
+
+;;; C/C++ support
+(defmethod ede-preprocessor-map ((this ede-generic-target-c-cpp))
+ "Get the pre-processor map for some generic C code."
+ (let* ((proj (ede-target-parent this))
+ (root (ede-project-root proj))
+ (config (ede-generic-get-configuration proj))
+ filemap
+ )
+ ;; Preprocessor files
+ (dolist (G (oref config :c-preprocessor-files))
+ (let ((table (semanticdb-file-table-object
+ (ede-expand-filename root G))))
+ (when table
+ (when (semanticdb-needs-refresh-p table)
+ (semanticdb-refresh-table table))
+ (setq filemap (append filemap (oref table lexical-table)))
+ )))
+ ;; The core table
+ (setq filemap (append filemap (oref config :c-preprocessor-table)))
+
+ filemap
+ ))
+
+(defmethod ede-system-include-path ((this ede-generic-target-c-cpp))
+ "Get the system include path used by project THIS."
+ (let* ((proj (ede-target-parent this))
+ (config (ede-generic-get-configuration proj)))
+ (oref config c-include-path)))
+
+;;; Customization
+;;
+(defmethod ede-customize ((proj ede-generic-project))
+ "Customize the EDE project PROJ."
+ (let ((config (ede-generic-get-configuration proj)))
+ (eieio-customize-object config)))
+
+(defmethod ede-customize ((target ede-generic-target))
+ "Customize the EDE TARGET."
+ ;; Nothing unique for the targets, use the project.
+ (ede-customize-project))
+
+(defmethod eieio-done-customizing ((config ede-generic-config))
+ "Called when EIEIO is done customizing the configuration object.
+We need to go back through the old buffers, and update them with
+the new configuration."
+ (ede-commit config)
+ ;; Loop over all the open buffers, and re-apply.
+ (ede-map-targets
+ (oref config project)
+ (lambda (target)
+ (ede-map-target-buffers
+ target
+ (lambda (b)
+ (with-current-buffer b
+ (ede-apply-target-options)))))))
+
+(defmethod ede-commit ((config ede-generic-config))
+ "Commit all changes to the configuration to disk."
+ (eieio-persistent-save config))
+
+;;; Creating Derived Projects:
+;;
+;; Derived projects need an autoloader so that EDE can find the
+;; different projects on disk.
+(defun ede-generic-new-autoloader (internal-name external-name
+ projectfile class)
+ "Add a new EDE Autoload instance for identifying a generic project.
+INTERNAL-NAME is a long name that identifies thsi project type.
+EXTERNAL-NAME is a shorter human readable name to describe the project.
+PROJECTFILE is a file name that identifies a project of this type to EDE, such as
+a Makefile, or SConstruct file.
+CLASS is the EIEIO class that is used to track this project. It should subclass
+the class `ede-generic-project' project."
+ (add-to-list 'ede-project-class-files
+ (ede-project-autoload internal-name
+ :name external-name
+ :file 'ede-generic
+ :proj-file projectfile
+ :load-type 'ede-generic-load
+ :class-sym class
+ :new-p nil)
+ ;; Generics must go at the end, since more specific types
+ ;; can create Makefiles also.
+ t))
+
+;;;###autoload
+(defun ede-enable-generic-projects ()
+ "Enable generic project loaders."
+ (interactive)
+ (ede-generic-new-autoloader "edeproject-makefile" "Make"
+ "Makefile" 'ede-generic-makefile-project)
+ (ede-generic-new-autoloader "edeproject-scons" "SCons"
+ "SConstruct" 'ede-generic-scons-project)
+ (ede-generic-new-autoloader "edeproject-cmake" "CMake"
+ "CMakeLists" 'ede-generic-cmake-project)
+ )
+
+
+;;; SPECIFIC TYPES OF GENERIC BUILDS
+;;
+
+;;; MAKEFILE
+
+(defclass ede-generic-makefile-project (ede-generic-project)
+ ((buildfile :initform "Makefile")
+ )
+ "Generic Project for makefiles.")
+
+(defmethod ede-generic-setup-configuration ((proj ede-generic-makefile-project) config)
+ "Setup a configuration for Make."
+ (oset config build-command "make -k")
+ (oset config debug-command "gdb ")
+ )
+
+
+;;; SCONS
+(defclass ede-generic-scons-project (ede-generic-project)
+ ((buildfile :initform "SConstruct")
+ )
+ "Generic Project for scons.")
+
+(defmethod ede-generic-setup-configuration ((proj ede-generic-scons-project) config)
+ "Setup a configuration for SCONS."
+ (oset config build-command "scons")
+ (oset config debug-command "gdb ")
+ )
+
+
+;;; CMAKE
+(defclass ede-generic-cmake-project (ede-generic-project)
+ ((buildfile :initform "CMakeLists")
+ )
+ "Generic Project for cmake.")
+
+(defmethod ede-generic-setup-configuration ((proj ede-generic-cmake-project) config)
+ "Setup a configuration for CMake."
+ (oset config build-command "cmake")
+ (oset config debug-command "gdb ")
+ )
+
+(provide 'ede/generic)
+
+;; Local variables:
+;; generated-autoload-file: "loaddefs.el"
+;; generated-autoload-load-name: "ede/generic"
+;; End:
+
+;;; ede/generic.el ends here