diff options
author | Chong Yidong <cyd@stupidchicken.com> | 2009-09-20 15:06:05 +0000 |
---|---|---|
committer | Chong Yidong <cyd@stupidchicken.com> | 2009-09-20 15:06:05 +0000 |
commit | acc332318e2c8687a8892b6e2da13e4a49ad9128 (patch) | |
tree | cbae36249128723cd5e311570623fd1b0aa40d31 | |
parent | 48bcb95a906dbbc27a1cd4db3cbde111346cbd87 (diff) | |
download | emacs-acc332318e2c8687a8892b6e2da13e4a49ad9128.tar.gz |
* cedet/ede.el, cedet/ede/*.el: New files.
* cedet/cedet.el: Require ede.
* cedet/semantic/symref/filter.el (semantic-symref-hits-in-region):
Require semantic/idle.
32 files changed, 9775 insertions, 2 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index bdd997453d8..ddcdab9cf3e 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,12 +1,18 @@ 2009-09-20 Chong Yidong <cyd@stupidchicken.com> + * cedet/ede.el, cedet/ede/*.el: New files. + + * cedet/cedet.el: Require ede. + * progmodes/autoconf.el: Provide autoconf as well. * files.el (auto-mode-alist): Use emacs-lisp-mode for Project.ede. * cedet/semantic/bovine/gcc.el (semantic-gcc-test-output-parser) (semantic-gcc-test-output-parser-this-machine): - * cedet/semantic/symref/filter.el (semantic-symref-test-count-hits-in-tag): + * cedet/semantic/symref/filter.el (semantic-symref-test-count-hits-in-tag) + (semantic-symref-hits-in-region): Require semantic/idle. + * cedet/semantic/db-global.el (semanticdb-test-gnu-global): * cedet/semantic/tag-write.el (semantic-tag-write-test) (semantic-tag-write-list-test): diff --git a/lisp/cedet/cedet.el b/lisp/cedet/cedet.el index 2ff55dc8258..2c3cc48b164 100644 --- a/lisp/cedet/cedet.el +++ b/lisp/cedet/cedet.el @@ -50,7 +50,7 @@ (require 'eieio) (require 'semantic) ;; (require 'srecode) -;; (require 'ede) +(require 'ede) (require 'speedbar) (defconst cedet-packages diff --git a/lisp/cedet/ede.el b/lisp/cedet/ede.el new file mode 100644 index 00000000000..6cae8d62bae --- /dev/null +++ b/lisp/cedet/ede.el @@ -0,0 +1,2013 @@ +;;; ede.el --- Emacs Development Environment gloss + +;;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, +;;; 2007, 2008, 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: +;; +;; EDE is the top level Lisp interface to a project management scheme +;; for Emacs. Emacs does many things well, including editing, +;; building, and debugging. Folks migrating from other IDEs don't +;; seem to think this qualifies, however, because they still have to +;; write the makefiles, and specify parameters to programs. +;; +;; This EDE mode will attempt to link these diverse programs together +;; into a comprehensive single interface, instead of a bunch of +;; different ones. + +;;; Install +;; +;; This command enables project mode on all files. +;; +;; (global-ede-mode t) + +(require 'eieio) +(require 'eieio-speedbar) +(require 'ede/source) +(require 'ede/loaddefs) + +(declare-function ede-convert-path "ede/files") +(declare-function ede-directory-get-open-project "ede/files") +(declare-function ede-directory-get-toplevel-open-project "ede/files") +(declare-function ede-directory-project-p "ede/files") +(declare-function ede-find-subproject-for-directory "ede/files") +(declare-function ede-project-directory-remove-hash "ede/files") +(declare-function ede-project-root "ede/files") +(declare-function ede-project-root-directory "ede/files") +(declare-function ede-toplevel "ede/files") +(declare-function ede-toplevel-project "ede/files") +(declare-function ede-up-directory "ede/files") +(declare-function data-debug-new-buffer "data-debug") +(declare-function data-debug-insert-object-slots "eieio-datadebug") +(declare-function semantic-lex-make-spp-table "semantic/lex-spp") + +(defconst ede-version "1.0pre7" + "Current version of the Emacs EDE.") + +;;; Code: +(defun ede-version () + "Display the current running version of EDE." + (interactive) (message "EDE %s" ede-version)) + +(defgroup ede nil + "Emacs Development Environment gloss." + :group 'tools + :group 'convenience + ) + +(defcustom ede-auto-add-method 'ask + "Whether a new source file shoud be automatically added to a target. +Whenever a new file is encountered in a directory controlled by a +project file, all targets are queried to see if it should be added. +If the value is 'always, then the new file is added to the first +target encountered. If the value is 'multi-ask, then if more than one +target wants the file, the user is asked. If only one target wants +the file, then then it is automatically added to that target. If the +value is 'ask, then the user is always asked, unless there is no +target willing to take the file. 'never means never perform the check." + :group 'ede + :type '(choice (const always) + (const multi-ask) + (const ask) + (const never))) + +(defcustom ede-debug-program-function 'gdb + "Default Emacs command used to debug a target." + :group 'ede + :type 'sexp) ; make this be a list of options some day + + +;;; Top level classes for projects and targets + +(defclass ede-project-autoload () + ((name :initarg :name + :documentation "Name of this project type") + (file :initarg :file + :documentation "The lisp file belonging to this class.") + (proj-file :initarg :proj-file + :documentation "Name of a project file of this type.") + (proj-root :initarg :proj-root + :type function + :documentation "A function symbol to call for the project root. +This function takes no arguments, and returns the current directories +root, if available. Leave blank to use the EDE directory walking +routine instead.") + (initializers :initarg :initializers + :initform nil + :documentation + "Initializers passed to the project object. +These are used so there can be multiple types of projects +associated with a single object class, based on the initilizeres used.") + (load-type :initarg :load-type + :documentation "Fn symbol used to load this project file.") + (class-sym :initarg :class-sym + :documentation "Symbol representing the project class to use.") + (new-p :initarg :new-p + :initform t + :documentation + "Non-nil if this is an option when a user creates a project.") + ) + "Class representing minimal knowledge set to run preliminary EDE functions. +When more advanced functionality is needed from a project type, that projects +type is required and the load function used.") + +(defvar ede-project-class-files + (list + (ede-project-autoload "edeproject-makefile" + :name "Make" :file 'ede/proj + :proj-file "Project.ede" + :load-type 'ede-proj-load + :class-sym 'ede-proj-project) + (ede-project-autoload "edeproject-automake" + :name "Automake" :file 'ede/proj + :proj-file "Project.ede" + :initializers '(:makefile-type Makefile.am) + :load-type 'ede-proj-load + :class-sym 'ede-proj-project) + (ede-project-autoload "automake" + :name "automake" :file 'ede/project-am + :proj-file "Makefile.am" + :load-type 'project-am-load + :class-sym 'project-am-makefile + :new-p nil) + (ede-project-autoload "cpp-root" + :name "CPP ROOT" :file 'ede/cpp-root + :proj-file 'ede-cpp-root-project-file-for-dir + :proj-root 'ede-cpp-root-project-root + :load-type 'ede-cpp-root-load + :class-sym 'ede-cpp-root + :new-p nil) + (ede-project-autoload "emacs" + :name "EMACS ROOT" :file 'ede/emacs + :proj-file "src/emacs.c" + :proj-root 'ede-emacs-project-root + :load-type 'ede-emacs-load + :class-sym 'ede-emacs-project + :new-p nil) + (ede-project-autoload "linux" + :name "LINUX ROOT" :file 'ede/linux + :proj-file "scripts/ver_linux" + :proj-root 'ede-linux-project-root + :load-type 'ede-linux-load + :class-sym 'ede-linux-project + :new-p nil) + (ede-project-autoload "simple-overlay" + :name "Simple" :file 'ede/simple + :proj-file 'ede-simple-projectfile-for-dir + :load-type 'ede-simple-load + :class-sym 'ede-simple-project)) + "List of vectos defining how to determine what type of projects exist.") + +;;; Generic project information manager objects + +(defclass ede-target (eieio-speedbar-directory-button) + ((buttonface :initform speedbar-file-face) ;override for superclass + (name :initarg :name + :type string + :custom string + :label "Name" + :group (default name) + :documentation "Name of this target.") + ;; @todo - I think this should be "dir", and not "path". + (path :initarg :path + :type string + ;:custom string + ;:label "Path to target" + ;:group (default name) + :documentation "The path to the sources of this target. +Relative to the path of the project it belongs to.") + (source :initarg :source + :initform nil + ;; I'd prefer a list of strings. + :type list + :custom (repeat (string :tag "File")) + :label "Source Files" + :group (default source) + :documentation "Source files in this target.") + (versionsource :initarg :versionsource + :initform nil + :type list + :custom (repeat (string :tag "File")) + :label "Source Files with Version String" + :group (source) + :documentation + "Source files with a version string in them. +These files are checked for a version string whenever the EDE version +of the master project is changed. When strings are found, the version +previously there is updated.") + ;; Class level slots + ;; +; (takes-compile-command :allocation :class +; :initarg :takes-compile-command +; :type boolean +; :initform nil +; :documentation +; "Non-nil if this target requires a user approved command.") + (sourcetype :allocation :class + :type list ;; list of symbols + :documentation + "A list of `ede-sourcecode' objects this class will handle. +This is used to match target objects with the compilers they can use, and +which files this object is interested in." + :accessor ede-object-sourcecode) + (keybindings :allocation :class + :initform (("D" . ede-debug-target)) + :documentation +"Keybindings specialized to this type of target." + :accessor ede-object-keybindings) + (menu :allocation :class + :initform ( [ "Debug target" ede-debug-target + (and ede-object + (obj-of-class-p ede-object ede-target)) ] + ) + :documentation "Menu specialized to this type of target." + :accessor ede-object-menu) + ) + "A top level target to build.") + +(defclass ede-project-placeholder (eieio-speedbar-directory-button) + ((name :initarg :name + :initform "Untitled" + :type string + :custom string + :label "Name" + :group (default name) + :documentation "The name used when generating distribution files.") + (version :initarg :version + :initform "1.0" + :type string + :custom string + :label "Version" + :group (default name) + :documentation "The version number used when distributing files.") + (directory :type string + :initarg :directory + :documentation "Directory this project is associated with.") + (dirinode :documentation "The inode id for :directory.") + (file :type string + :initarg :file + :documentation "File name where this project is stored.") + (rootproject ; :initarg - no initarg, don't save this slot! + :initform nil + :type (or null ede-project-placeholder-child) + :documentation "Pointer to our root project.") + ) + "Placeholder object for projects not loaded into memory. +Projects placeholders will be stored in a user specific location +and querying them will cause the actual project to get loaded.") + +(defclass ede-project (ede-project-placeholder) + ((subproj :initform nil + :type list + :documentation "Sub projects controlled by this project. +For Automake based projects, each directory is treated as a project.") + (targets :initarg :targets + :type list + :custom (repeat (object :objectcreatefcn ede-new-target-custom)) + :label "Local Targets" + :group (targets) + :documentation "List of top level targets in this project.") + (locate-obj :type (or null ede-locate-base-child) + :documentation + "A locate object to use as a backup to `ede-expand-filename'.") + (tool-cache :initarg :tool-cache + :type list + :custom (repeat object) + :label "Tool: " + :group tools + :documentation "List of tool cache configurations in this project. +This allows any tool to create, manage, and persist project-specific settings.") + (mailinglist :initarg :mailinglist + :initform "" + :type string + :custom string + :label "Mailing List Address" + :group name + :documentation + "An email address where users might send email for help.") + (web-site-url :initarg :web-site-url + :initform "" + :type string + :custom string + :label "Web Site URL" + :group name + :documentation "URL to this projects web site. +This is a URL to be sent to a web site for documentation.") + (web-site-directory :initarg :web-site-directory + :initform "" + :custom string + :label "Web Page Directory" + :group name + :documentation + "A directory where web pages can be found by Emacs. +For remote locations use a path compatible with ange-ftp or EFS. +You can also use TRAMP for use with rcp & scp.") + (web-site-file :initarg :web-site-file + :initform "" + :custom string + :label "Web Page File" + :group name + :documentation + "A file which contains the home page for this project. +This file can be relative to slot `web-site-directory'. +This can be a local file, use ange-ftp, EFS, or TRAMP.") + (ftp-site :initarg :ftp-site + :initform "" + :type string + :custom string + :label "FTP site" + :group name + :documentation + "FTP site where this project's distribution can be found. +This FTP site should be in Emacs form, as needed by `ange-ftp', but can +also be of a form used by TRAMP for use with scp, or rcp.") + (ftp-upload-site :initarg :ftp-upload-site + :initform "" + :type string + :custom string + :label "FTP Upload site" + :group name + :documentation + "FTP Site to upload new distributions to. +This FTP site should be in Emacs form as needed by `ange-ftp'. +If this slot is nil, then use `ftp-site' instead.") + (configurations :initarg :configurations + :initform ("debug" "release") + :type list + :custom (repeat string) + :label "Configuration Options" + :group (settings) + :documentation "List of available configuration types. +Individual target/project types can form associations between a configuration, +and target specific elements such as build variables.") + (configuration-default :initarg :configuration-default + :initform "debug" + :custom string + :label "Current Configuration" + :group (settings) + :documentation "The default configuration.") + (local-variables :initarg :local-variables + :initform nil + :custom (repeat (cons (sexp :tag "Variable") + (sexp :tag "Value"))) + :label "Project Local Variables" + :group (settings) + :documentation "Project local variables") + (keybindings :allocation :class + :initform (("D" . ede-debug-target)) + :documentation "Keybindings specialized to this type of target." + :accessor ede-object-keybindings) + (menu :allocation :class + :initform + ( + [ "Update Version" ede-update-version ede-object ] + [ "Version Control Status" ede-vc-project-directory ede-object ] + [ "Edit Project Homepage" ede-edit-web-page + (and ede-object (oref (ede-toplevel) web-site-file)) ] + [ "Browse Project URL" ede-web-browse-home + (and ede-object + (not (string= "" (oref (ede-toplevel) web-site-url)))) ] + "--" + [ "Rescan Project Files" ede-rescan-toplevel t ] + [ "Edit Projectfile" ede-edit-file-target + (and ede-object + (or (listp ede-object) + (not (obj-of-class-p ede-object ede-project)))) ] + ) + :documentation "Menu specialized to this type of target." + :accessor ede-object-menu) + ) + "Top level EDE project specification. +All specific project types must derive from this project." + :method-invocation-order :depth-first) + +;;; Management variables + +(defvar ede-projects nil + "A list of all active projects currently loaded in Emacs.") + +(defvar ede-object-root-project nil + "The current buffer's current root project. +If a file is under a project, this specifies the project that is at +the root of a project tree.") +(make-variable-buffer-local 'ede-object-root-project) + +(defvar ede-object-project nil + "The current buffer's current project at that level. +If a file is under a project, this specifies the project that contains the +current target.") +(make-variable-buffer-local 'ede-object-project) + +(defvar ede-object nil + "The current buffer's target object. +This object's class determines how to compile and debug from a buffer.") +(make-variable-buffer-local 'ede-object) + +(defvar ede-selected-object nil + "The currently user-selected project or target. +If `ede-object' is nil, then commands will operate on this object.") + +(defvar ede-constructing nil + "Non nil when constructing a project hierarchy.") + +(defvar ede-deep-rescan nil + "Non nil means scan down a tree, otherwise rescans are top level only. +Do not set this to non-nil globally. It is used internally.") + +;;; The EDE persistent cache. +;; +(defcustom ede-project-placeholder-cache-file + (expand-file-name "~/.projects.ede") + "File containing the list of projects EDE has viewed." + :group 'ede + :type 'file) + +(defvar ede-project-cache-files nil + "List of project files EDE has seen before.") + +(defun ede-save-cache () + "Save a cache of EDE objects that Emacs has seen before." + (interactive) + (let ((p ede-projects) + (c ede-project-cache-files) + (recentf-exclude '(ignore)) + ) + (condition-case nil + (progn + (set-buffer (find-file-noselect ede-project-placeholder-cache-file t)) + (erase-buffer) + (insert ";; EDE project cache file. +;; This contains a list of projects you have visited.\n(") + (while p + (when (and (car p) (ede-project-p p)) + (let ((f (oref (car p) file))) + (when (file-exists-p f) + (insert "\n \"" f "\"")))) + (setq p (cdr p))) + (while c + (insert "\n \"" (car c) "\"") + (setq c (cdr c))) + (insert "\n)\n") + (condition-case nil + (save-buffer 0) + (error + (message "File %s could not be saved." + ede-project-placeholder-cache-file))) + (kill-buffer (current-buffer)) + ) + (error + (message "File %s could not be read." + ede-project-placeholder-cache-file)) + + ))) + +(defun ede-load-cache () + "Load the cache of EDE projects." + (save-excursion + (let ((cachebuffer nil)) + (condition-case nil + (progn + (setq cachebuffer + (find-file-noselect ede-project-placeholder-cache-file t)) + (set-buffer cachebuffer) + (goto-char (point-min)) + (let ((c (read (current-buffer))) + (new nil) + (p ede-projects)) + ;; Remove loaded projects from the cache. + (while p + (setq c (delete (oref (car p) file) c)) + (setq p (cdr p))) + ;; Remove projects that aren't on the filesystem + ;; anymore. + (while c + (when (file-exists-p (car c)) + (setq new (cons (car c) new))) + (setq c (cdr c))) + ;; Save it + (setq ede-project-cache-files (nreverse new)))) + (error nil)) + (when cachebuffer (kill-buffer cachebuffer)) + ))) + +;;; Important macros for doing commands. +;; +(defmacro ede-with-projectfile (obj &rest forms) + "For the project in which OBJ resides, execute FORMS." + (list 'save-window-excursion + (list 'let* (list + (list 'pf + (list 'if (list 'obj-of-class-p + obj 'ede-target) + ;; @todo -I think I can change + ;; this to not need ede-load-project-file + ;; but I'm not sure how to test well. + (list 'ede-load-project-file + (list 'oref obj 'path)) + obj)) + '(dbka (get-file-buffer (oref pf file)))) + '(if (not dbka) (find-file (oref pf file)) + (switch-to-buffer dbka)) + (cons 'progn forms) + '(if (not dbka) (kill-buffer (current-buffer)))))) +(put 'ede-with-projectfile 'lisp-indent-function 1) + + +;;; Prompting +;; +(defun ede-singular-object (prompt) + "Using PROMPT, choose a single object from the current buffer." + (if (listp ede-object) + (ede-choose-object prompt ede-object) + ede-object)) + +(defun ede-choose-object (prompt list-o-o) + "Using PROMPT, ask the user which OBJECT to use based on the name field. +Argument LIST-O-O is the list of objects to choose from." + (let* ((al (object-assoc-list 'name list-o-o)) + (ans (completing-read prompt al nil t))) + (setq ans (assoc ans al)) + (cdr ans))) + +;;; Menu and Keymap + +(defvar ede-minor-mode nil + "Non-nil in EDE controlled buffers.") +(make-variable-buffer-local 'ede-minor-mode) + +;; We don't want to waste space. There is a menu after all. +(add-to-list 'minor-mode-alist '(ede-minor-mode "")) + +(defvar ede-minor-keymap + (let ((map (make-sparse-keymap)) + (pmap (make-sparse-keymap))) + (define-key pmap "e" 'ede-edit-file-target) + (define-key pmap "a" 'ede-add-file) + (define-key pmap "d" 'ede-remove-file) + (define-key pmap "t" 'ede-new-target) + (define-key pmap "g" 'ede-rescan-toplevel) + (define-key pmap "s" 'ede-speedbar) + (define-key pmap "l" 'ede-load-project-file) + (define-key pmap "f" 'ede-find-file) + (define-key pmap "C" 'ede-compile-project) + (define-key pmap "c" 'ede-compile-target) + (define-key pmap "\C-c" 'ede-compile-selected) + (define-key pmap "D" 'ede-debug-target) + ;; bind our submap into map + (define-key map "\C-c." pmap) + map) + "Keymap used in project minor mode.") + +(if ede-minor-keymap + (progn + (easy-menu-define + ede-minor-menu ede-minor-keymap "Project Minor Mode Menu" + '("Project" + ( "Build" :filter ede-build-forms-menu ) + ( "Project Options" :filter ede-project-forms-menu ) + ( "Target Options" :filter ede-target-forms-menu ) + [ "Create Project" ede-new (not ede-object) ] + [ "Load a project" ede t ] +;; [ "Select Active Target" 'undefined nil ] +;; [ "Remove Project" 'undefined nil ] + "---" + [ "Find File in Project..." ede-find-file t ] + ( "Customize" :filter ede-customize-forms-menu ) + [ "View Project Tree" ede-speedbar t ] + )) + )) + +;; Allow re-insertion of a new keymap +(let ((a (assoc 'ede-minor-mode minor-mode-map-alist))) + (if a + (setcdr a ede-minor-keymap) + (add-to-list 'minor-mode-map-alist + (cons 'ede-minor-mode ede-minor-keymap)) + )) + +(defun ede-menu-obj-of-class-p (class) + "Return non-nil if some member of `ede-object' is a child of CLASS." + (if (listp ede-object) + (ede-or (mapcar (lambda (o) (obj-of-class-p o class)) ede-object)) + (obj-of-class-p ede-object class))) + +(defun ede-build-forms-menu (menu-def) + "Create a sub menu for building different parts of an EDE system. +Argument MENU-DEF is the menu definition to use." + (easy-menu-filter-return + (easy-menu-create-menu + "Build Forms" + (let ((obj (ede-current-project)) + (newmenu nil) ;'([ "Build Selected..." ede-compile-selected t ])) + targets + targitems + ede-obj + (tskip nil)) + (if (not obj) + nil + (setq targets (when (slot-boundp obj 'targets) + (oref obj targets)) + ede-obj (if (listp ede-object) ede-object (list ede-object))) + ;; First, collect the build items from the project + (setq newmenu (append newmenu (ede-menu-items-build obj t))) + ;; Second, Declare the current target menu items + (if (and ede-obj (ede-menu-obj-of-class-p ede-target)) + (while ede-obj + (setq newmenu (append newmenu + (ede-menu-items-build (car ede-obj) t)) + tskip (car ede-obj) + ede-obj (cdr ede-obj)))) + ;; Third, by name, enable builds for other local targets + (while targets + (unless (eq tskip (car targets)) + (setq targitems (ede-menu-items-build (car targets) nil)) + (setq newmenu + (append newmenu + (if (= 1 (length targitems)) + targitems + (cons (ede-name (car targets)) + targitems)))) + ) + (setq targets (cdr targets))) + ;; Fourth, build sub projects. + ;; -- nerp + ;; Fifth, Add make distribution + (append newmenu (list [ "Make distribution" ede-make-dist t ])) + ))))) + +(defun ede-target-forms-menu (menu-def) + "Create a target MENU-DEF based on the object belonging to this buffer." + (easy-menu-filter-return + (easy-menu-create-menu + "Target Forms" + (let ((obj (or ede-selected-object ede-object))) + (append + '([ "Add File" ede-add-file (ede-current-project) ] + [ "Remove File" ede-remove-file + (and ede-object + (or (listp ede-object) + (not (obj-of-class-p ede-object ede-project)))) ] + "-") + (if (not obj) + nil + (if (and (not (listp obj)) (oref obj menu)) + (oref obj menu) + (when (listp obj) + ;; This is bad, but I'm not sure what else to do. + (oref (car obj) menu))))))))) + +(defun ede-project-forms-menu (menu-def) + "Create a target MENU-DEF based on the object belonging to this buffer." + (easy-menu-filter-return + (easy-menu-create-menu + "Project Forms" + (let* ((obj (ede-current-project)) + (class (if obj (object-class obj))) + (menu nil)) + (condition-case err + (progn + (while (and class (slot-exists-p class 'menu)) + ;;(message "Looking at class %S" class) + (setq menu (append menu (oref class menu)) + class (class-parent class)) + (if (listp class) (setq class (car class)))) + (append + '( [ "Add Target" ede-new-target (ede-current-project) ] + [ "Remove Target" ede-delete-target ede-object ] + "-") + menu + )) + (error (message "Err found: %S" err) + menu) + ))))) + +(defun ede-customize-forms-menu (menu-def) + "Create a menu of the project, and targets that can be customized. +Argument MENU-DEF is the definition of the current menu." + (easy-menu-filter-return + (easy-menu-create-menu + "Customize Project" + (let* ((obj (ede-current-project)) + (targ (when (slot-boundp obj 'targets) + (oref obj targets)))) + (when obj + ;; Make custom menus for everything here. + (append (list + (cons (concat "Project " (ede-name obj)) + (eieio-customize-object-group obj)) + [ "Reorder Targets" ede-project-sort-targets t ] + ) + (mapcar (lambda (o) + (cons (concat "Target " (ede-name o)) + (eieio-customize-object-group o))) + targ))))))) + + +(defun ede-apply-object-keymap (&optional default) + "Add target specific keybindings into the local map. +Optional argument DEFAULT indicates if this should be set to the default +version of the keymap." + (let ((object (or ede-object ede-selected-object))) + (condition-case nil + (let ((keys (ede-object-keybindings object))) + (while keys + (local-set-key (concat "\C-c." (car (car keys))) + (cdr (car keys))) + (setq keys (cdr keys)))) + (error nil)))) + +;;; Menu building methods for building +;; +(defmethod ede-menu-items-build ((obj ede-project) &optional current) + "Return a list of menu items for building project OBJ. +If optional argument CURRENT is non-nil, return sub-menu code." + (if current + (list [ "Build Current Project" ede-compile-project t ]) + (list (vector + (list + (concat "Build Project " (ede-name obj)) + `(project-compile-project ,obj)))))) + +(defmethod ede-menu-items-build ((obj ede-target) &optional current) + "Return a list of menu items for building target OBJ. +If optional argument CURRENT is non-nil, return sub-menu code." + (if current + (list [ "Build Current Target" ede-compile-target t ]) + (list (vector + (concat "Build Target " (ede-name obj)) + `(project-compile-target ,obj) + t)))) + +;;; Mode Declarations +;; +(eval-and-compile + (autoload 'ede-dired-minor-mode "ede-dired" "EDE commands for dired" t)) + +(defun ede-apply-target-options () + "Apply options to the current buffer for the active project/target." + (if (ede-current-project) + (ede-set-project-variables (ede-current-project))) + (ede-apply-object-keymap) + (ede-apply-preprocessor-map) + ) + +(defun ede-turn-on-hook () + "Turn on EDE minor mode in the current buffer if needed. +To be used in hook functions." + (if (or (and (stringp (buffer-file-name)) + (stringp default-directory)) + ;; Emacs 21 has no buffer file name for directory edits. + ;; so we need to add these hacks in. + (eq major-mode 'dired-mode) + (eq major-mode 'vc-dired-mode)) + (ede-minor-mode 1))) + +(defun ede-minor-mode (&optional arg) + "Project minor mode. +If this file is contained, or could be contained in an EDE +controlled project, then this mode should be active. + +With argument ARG positive, turn on the mode. Negative, turn off the +mode. nil means to toggle the mode." + (interactive "P") + (if (or (eq major-mode 'dired-mode) + (eq major-mode 'vc-dired-mode)) + (ede-dired-minor-mode arg) + (progn + (setq ede-minor-mode + (not (or (and (null arg) ede-minor-mode) + (<= (prefix-numeric-value arg) 0)))) + (if (and ede-minor-mode (not ede-constructing) + (ede-directory-project-p default-directory t)) + (let* ((ROOT nil) + (proj (ede-directory-get-open-project default-directory + 'ROOT))) + (when (not proj) + ;; @todo - this could be wasteful. + (setq proj (ede-load-project-file default-directory 'ROOT))) + + (setq ede-object-project proj) + (setq ede-object-root-project + (or ROOT (ede-project-root proj))) + (setq ede-object (ede-buffer-object)) + (if (and (not ede-object) ede-object-project) + (ede-auto-add-to-target)) + (ede-apply-target-options)) + ;; If we fail to have a project here, turn it back off. + (if (not (interactive-p)) + (setq ede-minor-mode nil)))))) + +(defun ede-reset-all-buffers (onoff) + "Reset all the buffers due to change in EDE. +ONOFF indicates enabling or disabling the mode." + (let ((b (buffer-list))) + (while b + (when (buffer-file-name (car b)) + (ede-buffer-object (car b)) + ) + (setq b (cdr b))))) + +;;;###autoload +(defun global-ede-mode (arg) + "Turn on variable `ede-minor-mode' mode when ARG is positive. +If ARG is negative, disable. Toggle otherwise." + (interactive "P") + (if (not arg) + (if (member 'ede-turn-on-hook find-file-hook) + (global-ede-mode -1) + (global-ede-mode 1)) + (if (or (eq arg t) (> arg 0)) + (progn + (add-hook 'semanticdb-project-predicate-functions 'ede-directory-project-p) + (add-hook 'semanticdb-project-root-functions 'ede-toplevel-project-or-nil) + (add-hook 'ecb-source-path-functions 'ede-ecb-project-paths) + (add-hook 'find-file-hook 'ede-turn-on-hook) + (add-hook 'dired-mode-hook 'ede-turn-on-hook) + (add-hook 'kill-emacs-hook 'ede-save-cache) + (ede-load-cache)) + (remove-hook 'semanticdb-project-predicate-functions 'ede-directory-project-p) + (remove-hook 'semanticdb-project-root-functions 'ede-toplevel-project-or-nil) + (remove-hook 'ecb-source-path-functions 'ede-ecb-project-paths) + (remove-hook 'find-file-hook 'ede-turn-on-hook) + (remove-hook 'dired-mode-hook 'ede-turn-on-hook) + (remove-hook 'kill-emacs-hook 'ede-save-cache) + (ede-save-cache)) + (ede-reset-all-buffers arg))) + +(defvar ede-ignored-file-alist + '( "\\.cvsignore$" + "\\.#" + "~$" + ) + "List of file name patters that EDE will never ask about.") + +(defun ede-ignore-file (filename) + "Should we ignore FILENAME?" + (let ((any nil) + (F ede-ignored-file-alist)) + (while (and (not any) F) + (when (string-match (car F) filename) + (setq any t)) + (setq F (cdr F))) + any)) + +(defun ede-auto-add-to-target () + "Look for a target that wants to own the current file. +Follow the preference set with `ede-auto-add-method' and get the list +of objects with the `ede-want-file-p' method." + (if ede-object (error "Ede-object already defined for %s" (buffer-name))) + (if (or (eq ede-auto-add-method 'never) + (ede-ignore-file (buffer-file-name))) + nil + (let (wants desires) + ;; Find all the objects. + (setq wants (oref (ede-current-project) targets)) + (while wants + (if (ede-want-file-p (car wants) (buffer-file-name)) + (setq desires (cons (car wants) desires))) + (setq wants (cdr wants))) + (if desires + (cond ((or (eq ede-auto-add-method 'ask) + (and (eq ede-auto-add-method 'multi-ask) + (< 1 (length desires)))) + (let* ((al (append + ;; some defaults + '(("none" . nil) + ("new target" . new)) + ;; If we are in an unparented subdir, + ;; offer new a subproject + (if (ede-directory-project-p default-directory) + () + '(("create subproject" . project))) + ;; Here are the existing objects we want. + (object-assoc-list 'name desires))) + (case-fold-search t) + (ans (completing-read + (format "Add %s to target: " (buffer-file-name)) + al nil t))) + (setq ans (assoc ans al)) + (cond ((eieio-object-p (cdr ans)) + (ede-add-file (cdr ans))) + ((eq (cdr ans) 'new) + (ede-new-target)) + (t nil)))) + ((or (eq ede-auto-add-method 'always) + (and (eq ede-auto-add-method 'multi-ask) + (= 1 (length desires)))) + (ede-add-file (car desires))) + (t nil)))))) + + +;;; Interactive method invocations +;; +(defun ede (file) + "Start up EDE on something. +Argument FILE is the file or directory to load a project from." + (interactive "fProject File: ") + (if (not (file-exists-p file)) + (ede-new file) + (ede-load-project-file (file-name-directory file)))) + +(defun ede-new (type &optional name) + "Create a new project starting of project type TYPE. +Optional argument NAME is the name to give this project." + (interactive + (list (completing-read "Project Type: " + (object-assoc-list + 'name + (let* ((l ede-project-class-files) + (cp (ede-current-project)) + (cs (when cp (object-class cp))) + (r nil)) + (while l + (if cs + (if (eq (oref (car l) :class-sym) + cs) + (setq r (cons (car l) r))) + (if (oref (car l) new-p) + (setq r (cons (car l) r)))) + (setq l (cdr l))) + (when (not r) + (if cs + (error "No valid interactive sub project types for %s" + cs) + (error "EDE error: Can't fin project types to create"))) + r) + ) + nil t))) + ;; Make sure we have a valid directory + (when (not (file-exists-p default-directory)) + (error "Cannot create project in non-existant directory %s" default-directory)) + (when (not (file-writable-p default-directory)) + (error "No write permissions for %s" default-directory)) + ;; Create the project + (let* ((obj (object-assoc type 'name ede-project-class-files)) + (nobj (let ((f (oref obj file)) + (pf (oref obj proj-file))) + ;; We are about to make something new, changing the + ;; state of existing directories. + (ede-project-directory-remove-hash default-directory) + ;; Make sure this class gets loaded! + (require f) + (make-instance (oref obj class-sym) + :name (or name (read-string "Name: ")) + :directory default-directory + :file (cond ((stringp pf) + (expand-file-name pf)) + ((fboundp pf) + (funcall pf)) + (t + (error + "Unknown file name specifier %S" + pf))) + :targets nil))) + (inits (oref obj initializers))) + ;; Force the name to match for new objects. + (object-set-name-string nobj (oref nobj :name)) + ;; Handle init args. + (while inits + (eieio-oset nobj (car inits) (car (cdr inits))) + (setq inits (cdr (cdr inits)))) + (let ((pp (ede-parent-project))) + (when pp + (ede-add-subproject pp nobj) + (ede-commit-project pp))) + (ede-commit-project nobj)) + ;; Have the menu appear + (setq ede-minor-mode t) + ;; Allert the user + (message "Project created and saved. You may now create targets.")) + +(defmethod ede-add-subproject ((proj-a ede-project) proj-b) + "Add into PROJ-A, the subproject PROJ-B." + (oset proj-a subproj (cons proj-b (oref proj-a subproj)))) + +(defmethod ede-subproject-relative-path ((proj ede-project) &optional parent-in) + "Get a path name for PROJ which is relative to the parent project. +If PARENT is specified, then be relative to the PARENT project. +Specifying PARENT is useful for sub-sub projects relative to the root project." + (let* ((parent (or parent-in (ede-parent-project proj))) + (dir (file-name-directory (oref proj file)))) + (if (and parent (not (eq parent proj))) + (file-relative-name dir (file-name-directory (oref parent file))) + ""))) + +(defmethod ede-subproject-p ((proj ede-project)) + "Return non-nil if PROJ is a sub project." + (ede-parent-project proj)) + +(defun ede-invoke-method (sym &rest args) + "Invoke method SYM on the current buffer's project object. +ARGS are additional arguments to pass to method sym." + (if (not ede-object) + (error "Cannot invoke %s for %s" (symbol-name sym) + (buffer-name))) + ;; Always query a target. There should never be multiple + ;; projects in a single buffer. + (apply sym (ede-singular-object "Target: ") args)) + +(defun ede-rescan-toplevel () + "Rescan all project files." + (interactive) + (let ((toppath (ede-toplevel-project default-directory)) + (ede-deep-rescan t)) + (project-rescan (ede-load-project-file toppath)) + (ede-reset-all-buffers 1) + )) + +(defun ede-new-target (&rest args) + "Create a new target specific to this type of project file. +Different projects accept different arguments ARGS. +Typically you can specify NAME, target TYPE, and AUTOADD, where AUTOADD is +a string \"y\" or \"n\", which answers the y/n question done interactively." + (interactive) + (apply 'project-new-target (ede-current-project) args) + (setq ede-object nil) + (setq ede-object (ede-buffer-object (current-buffer))) + (ede-apply-target-options)) + +(defun ede-new-target-custom () + "Create a new target specific to this type of project file." + (interactive) + (project-new-target-custom (ede-current-project))) + +(defun ede-delete-target (target) + "Delete TARGET from the current project." + (interactive (list + (let ((ede-object (ede-current-project))) + (ede-invoke-method 'project-interactive-select-target + "Target: ")))) + ;; Find all sources in buffers associated with the condemned buffer. + (let ((condemned (ede-target-buffers target))) + (project-delete-target target) + ;; Loop over all project controlled buffers + (save-excursion + (while condemned + (set-buffer (car condemned)) + (setq ede-object nil) + (setq ede-object (ede-buffer-object (current-buffer))) + (setq condemned (cdr condemned)))) + (ede-apply-target-options))) + +(defun ede-add-file (target) + "Add the current buffer to a TARGET in the current project." + (interactive (list + (let ((ede-object (ede-current-project))) + (ede-invoke-method 'project-interactive-select-target + "Target: ")))) + (when (stringp target) + (let* ((proj (ede-current-project)) + (ob (object-assoc-list 'name (oref proj targets)))) + (setq target (cdr (assoc target ob))))) + + (when (not target) + (error "Could not find specified target %S" target)) + + (project-add-file target (buffer-file-name)) + (setq ede-object nil) + (setq ede-object (ede-buffer-object (current-buffer))) + (when (not ede-object) + (error "Can't add %s to target %s: Wrong file type" + (file-name-nondirectory (buffer-file-name)) + (object-name target))) + (ede-apply-target-options)) + +(defun ede-remove-file (&optional force) + "Remove the current file from targets. +Optional argument FORCE forces the file to be removed without asking." + (interactive "P") + (if (not ede-object) + (error "Cannot invoke remove-file for %s" (buffer-name))) + (let ((eo (if (listp ede-object) + (prog1 + ede-object + (setq force nil)) + (list ede-object)))) + (while eo + (if (or force (y-or-n-p (format "Remove from %s? " (ede-name (car eo))))) + (project-remove-file (car eo) (buffer-file-name))) + (setq eo (cdr eo))) + (setq ede-object nil) + (setq ede-object (ede-buffer-object (current-buffer))) + (ede-apply-target-options))) + +(defun ede-edit-file-target () + "Enter the project file to hand edit the current buffer's target." + (interactive) + (ede-invoke-method 'project-edit-file-target)) + +(defun ede-compile-project () + "Compile the current project." + (interactive) + ;; @TODO - This just wants the root. There should be a better way. + (let ((cp (ede-current-project))) + (while (ede-parent-project cp) + (setq cp (ede-parent-project cp))) + (let ((ede-object cp)) + (ede-invoke-method 'project-compile-project)))) + +(defun ede-compile-selected (target) + "Compile some TARGET from the current project." + (interactive (list (project-interactive-select-target (ede-current-project) + "Target to Build: "))) + (project-compile-target target)) + +(defun ede-compile-target () + "Compile the current buffer's associated target." + (interactive) + (ede-invoke-method 'project-compile-target)) + +(defun ede-debug-target () + "Debug the current buffer's assocated target." + (interactive) + (ede-invoke-method 'project-debug-target)) + +(defun ede-make-dist () + "Create a distribution from the current project." + (interactive) + (let ((ede-object (ede-current-project))) + (ede-invoke-method 'project-make-dist))) + +;;; Customization +;; +;; Routines for customizing projects and targets. + +(defvar eieio-ede-old-variables nil + "The old variables for a project.") + +(defalias 'customize-project 'ede-customize-project) +(defun ede-customize-project (&optional group) + "Edit fields of the current project through EIEIO & Custom. +Optional GROUP specifies the subgroup of slots to customize." + (interactive "P") + (require 'eieio-custom) + (let* ((ov (oref (ede-current-project) local-variables)) + (cp (ede-current-project)) + (group (if group (eieio-read-customization-group cp)))) + (eieio-customize-object cp group) + (make-local-variable 'eieio-ede-old-variables) + (setq eieio-ede-old-variables ov))) + +(defalias 'customize-target 'ede-customize-current-target) +(defun ede-customize-current-target(&optional group) + "Edit fields of the current target through EIEIO & Custom. +Optional argument OBJ is the target object to customize. +Optional argument GROUP is the slot group to display." + (interactive "P") + (require 'eieio-custom) + (if (not (obj-of-class-p ede-object ede-target)) + (error "Current file is not part of a target.")) + (let ((group (if group (eieio-read-customization-group ede-object)))) + (ede-customize-target ede-object group))) + +(defun ede-customize-target (obj group) + "Edit fields of the current target through EIEIO & Custom. +Optional argument OBJ is the target object to customize. +Optional argument GROUP is the slot group to display." + (require 'eieio-custom) + (if (and obj (not (obj-of-class-p obj ede-target))) + (error "No logical target to customize")) + (eieio-customize-object obj (or group 'default))) +;;; Target Sorting +;; +;; Target order can be important, but custom doesn't support a way +;; to resort items in a list. This function by David Engster allows +;; targets to be re-arranged. + +(defvar ede-project-sort-targets-order nil + "Variable for tracking target order in `ede-project-sort-targets'.") + +(defun ede-project-sort-targets () + "Create a custom-like buffer for sorting targets of current project." + (interactive) + (let ((proj (ede-current-project)) + (count 1) + current order) + (switch-to-buffer (get-buffer-create "*EDE sort targets*")) + (erase-buffer) + (setq ede-object-project proj) + (widget-create 'push-button + :notify (lambda (&rest ignore) + (let ((targets (oref ede-object-project targets)) + cur newtargets) + (while (setq cur (pop ede-project-sort-targets-order)) + (setq newtargets (append newtargets + (list (nth cur targets))))) + (oset ede-object-project targets newtargets)) + (ede-commit-project ede-object-project) + (kill-buffer)) + " Accept ") + (widget-insert " ") + (widget-create 'push-button + :notify (lambda (&rest ignore) + (kill-buffer)) + " Cancel ") + (widget-insert "\n\n") + (setq ede-project-sort-targets-order nil) + (mapc (lambda (x) + (add-to-ordered-list + 'ede-project-sort-targets-order + x x)) + (number-sequence 0 (1- (length (oref proj targets))))) + (ede-project-sort-targets-list) + (use-local-map widget-keymap) + (widget-setup) + (goto-char (point-min)))) + +(defun ede-project-sort-targets-list () + "Sort the target list while using `ede-project-sort-targets'." + (save-excursion + (let ((count 0) + (targets (oref ede-object-project targets)) + (inhibit-read-only t) + (inhibit-modification-hooks t)) + (goto-char (point-min)) + (forward-line 2) + (delete-region (point) (point-max)) + (while (< count (length targets)) + (if (> count 0) + (widget-create 'push-button + :notify `(lambda (&rest ignore) + (let ((cur ede-project-sort-targets-order)) + (add-to-ordered-list + 'ede-project-sort-targets-order + (nth ,count cur) + (1- ,count)) + (add-to-ordered-list + 'ede-project-sort-targets-order + (nth (1- ,count) cur) ,count)) + (ede-project-sort-targets-list)) + " Up ") + (widget-insert " ")) + (if (< count (1- (length targets))) + (widget-create 'push-button + :notify `(lambda (&rest ignore) + (let ((cur ede-project-sort-targets-order)) + (add-to-ordered-list + 'ede-project-sort-targets-order + (nth ,count cur) (1+ ,count)) + (add-to-ordered-list + 'ede-project-sort-targets-order + (nth (1+ ,count) cur) ,count)) + (ede-project-sort-targets-list)) + " Down ") + (widget-insert " ")) + (widget-insert (concat " " (number-to-string (1+ count)) ".: " + (oref (nth (nth count ede-project-sort-targets-order) + targets) name) "\n")) + (setq count (1+ count)))))) + +;;; Customization hooks +;; +;; These hooks are used when finishing up a customization. +(defmethod eieio-done-customizing ((proj ede-project)) + "Call this when a user finishes customizing PROJ." + (let ((ov eieio-ede-old-variables) + (nv (oref proj local-variables))) + (setq eieio-ede-old-variables nil) + (while ov + (if (not (assoc (car (car ov)) nv)) + (save-excursion + (mapc (lambda (b) + (set-buffer b) + (kill-local-variable (car (car ov)))) + (ede-project-buffers proj)))) + (setq ov (cdr ov))) + (mapc (lambda (b) (ede-set-project-variables proj b)) + (ede-project-buffers proj)))) + +(defmethod eieio-done-customizing ((target ede-target)) + "Call this when a user finishes customizing TARGET." + nil) + +(defmethod ede-commit-project ((proj ede-project)) + "Commit any change to PROJ to its file." + nil + ) + + +;;; EDE project placeholder methods +;; +(defmethod ede-project-force-load ((this ede-project-placeholder)) + "Make sure the placeholder THIS is replaced with the real thing. +Return the new object created in its place." + this + ) + + +;;; EDE project target baseline methods. +;; +;; If you are developing a new project type, you need to implement +;; all of these methods, unless, of course, they do not make sense +;; for your particular project. +;; +;; Your targets should inherit from `ede-target', and your project +;; files should inherit from `ede-project'. Create the appropriate +;; methods based on those below. + +(defmethod project-interactive-select-target ((this ede-project-placeholder) prompt) + ; checkdoc-params: (prompt) + "Make sure placeholder THIS is replaced with the real thing, and pass through." + (project-interactive-select-target (ede-project-force-load this) prompt)) + +(defmethod project-interactive-select-target ((this ede-project) prompt) + "Interactively query for a target that exists in project THIS. +Argument PROMPT is the prompt to use when querying the user for a target." + (let ((ob (object-assoc-list 'name (oref this targets)))) + (cdr (assoc (completing-read prompt ob nil t) ob)))) + +(defmethod project-add-file ((this ede-project-placeholder) file) + ; checkdoc-params: (file) + "Make sure placeholder THIS is replaced with the real thing, and pass through." + (project-add-file (ede-project-force-load this) file)) + +(defmethod project-add-file ((ot ede-target) file) + "Add the current buffer into project project target OT. +Argument FILE is the file to add." + (error "add-file not supported by %s" (object-name ot))) + +(defmethod project-remove-file ((ot ede-target) fnnd) + "Remove the current buffer from project target OT. +Argument FNND is an argument." + (error "remove-file not supported by %s" (object-name ot))) + +(defmethod project-edit-file-target ((ot ede-target)) + "Edit the target OT associated w/ this file." + (find-file (oref (ede-current-project) file))) + +(defmethod project-new-target ((proj ede-project) &rest args) + "Create a new target. It is up to the project PROJ to get the name." + (error "new-target not supported by %s" (object-name proj))) + +(defmethod project-new-target-custom ((proj ede-project)) + "Create a new target. It is up to the project PROJ to get the name." + (error "New-target-custom not supported by %s" (object-name proj))) + +(defmethod project-delete-target ((ot ede-target)) + "Delete the current target OT from it's parent project." + (error "add-file not supported by %s" (object-name ot))) + +(defmethod project-compile-project ((obj ede-project) &optional command) + "Compile the entire current project OBJ. +Argument COMMAND is the command to use when compiling." + (error "compile-project not supported by %s" (object-name obj))) + +(defmethod project-compile-target ((obj ede-target) &optional command) + "Compile the current target OBJ. +Argument COMMAND is the command to use for compiling the target." + (error "compile-target not supported by %s" (object-name obj))) + +(defmethod project-debug-target ((obj ede-target)) + "Run the current project target OBJ in a debugger." + (error "debug-target not supported by %s" (object-name obj))) + +(defmethod project-make-dist ((this ede-project)) + "Build a distribution for the project based on THIS project." + (error "Make-dist not supported by %s" (object-name this))) + +(defmethod project-dist-files ((this ede-project)) + "Return a list of files that constitutes a distribution of THIS project." + (error "Dist-files is not supported by %s" (object-name this))) + +(defmethod project-rescan ((this ede-project)) + "Rescan the EDE proj project THIS." + (error "Rescanning a project is not supported by %s" (object-name this))) + +;;; Default methods for EDE classes +;; +;; These are methods which you might want to override, but there is +;; no need to in most situations because they are either a) simple, or +;; b) cosmetic. + +(defmethod ede-name ((this ede-target)) + "Return the name of THIS targt." + (oref this name)) + +(defmethod ede-target-name ((this ede-target)) + "Return the name of THIS target, suitable for make or debug style commands." + (oref this name)) + +(defmethod ede-name ((this ede-project)) + "Return a short-name for THIS project file. +Do this by extracting the lowest directory name." + (oref this name)) + +(defmethod ede-description ((this ede-project)) + "Return a description suitable for the minibuffer about THIS." + (format "Project %s: %d subprojects, %d targets." + (ede-name this) (length (oref this subproj)) + (length (oref this targets)))) + +(defmethod ede-description ((this ede-target)) + "Return a description suitable for the minibuffer about THIS." + (format "Target %s: with %d source files." + (ede-name this) (length (oref this source)))) + +(defmethod ede-want-file-p ((this ede-target) file) + "Return non-nil if THIS target wants FILE." + ;; By default, all targets reference the source object, and let it decide. + (let ((src (ede-target-sourcecode this))) + (while (and src (not (ede-want-file-p (car src) file))) + (setq src (cdr src))) + src)) + +(defmethod ede-want-file-source-p ((this ede-target) file) + "Return non-nil if THIS target wants FILE." + ;; By default, all targets reference the source object, and let it decide. + (let ((src (ede-target-sourcecode this))) + (while (and src (not (ede-want-file-source-p (car src) file))) + (setq src (cdr src))) + src)) + +(defun ede-header-file () + "Return the header file for the current buffer. +Not all buffers need headers, so return nil if no applicable." + (if ede-object + (ede-buffer-header-file ede-object (current-buffer)) + nil)) + +(defmethod ede-buffer-header-file ((this ede-project) buffer) + "Return nil, projects don't have header files." + nil) + +(defmethod ede-buffer-header-file ((this ede-target) buffer) + "There are no default header files in EDE. +Do a quick check to see if there is a Header tag in this buffer." + (save-excursion + (set-buffer buffer) + (if (re-search-forward "::Header:: \\([a-zA-Z0-9.]+\\)" nil t) + (buffer-substring-no-properties (match-beginning 1) + (match-end 1)) + (let ((src (ede-target-sourcecode this)) + (found nil)) + (while (and src (not found)) + (setq found (ede-buffer-header-file (car src) (buffer-file-name)) + src (cdr src))) + found)))) + +(defun ede-documentation-files () + "Return the documentation files for the current buffer. +Not all buffers need documentations, so return nil if no applicable. +Some projects may have multiple documentation files, so return a list." + (if ede-object + (ede-buffer-documentation-files ede-object (current-buffer)) + nil)) + +(defmethod ede-buffer-documentation-files ((this ede-project) buffer) + "Return all documentation in project THIS based on BUFFER." + ;; Find the info node. + (ede-documentation this)) + +(defmethod ede-buffer-documentation-files ((this ede-target) buffer) + "Check for some documentation files for THIS. +Also do a quick check to see if there is a Documentation tag in this BUFFER." + (save-excursion + (set-buffer buffer) + (if (re-search-forward "::Documentation:: \\([a-zA-Z0-9.]+\\)" nil t) + (buffer-substring-no-properties (match-beginning 1) + (match-end 1)) + ;; Check the master project + (let ((cp (ede-toplevel))) + (ede-buffer-documentation-files cp (current-buffer)))))) + +(defmethod ede-documentation ((this ede-project)) + "Return a list of files that provides documentation. +Documentation is not for object THIS, but is provided by THIS for other +files in the project." + (let ((targ (oref this targets)) + (proj (oref this subproj)) + (found nil)) + (while targ + (setq found (append (ede-documentation (car targ)) found) + targ (cdr targ))) + (while proj + (setq found (append (ede-documentation (car proj)) found) + proj (cdr proj))) + found)) + +(defmethod ede-documentation ((this ede-target)) + "Return a list of files that provides documentation. +Documentation is not for object THIS, but is provided by THIS for other +files in the project." + nil) + +(defun ede-html-documentation-files () + "Return a list of HTML documentation files associated with this project." + (ede-html-documentation (ede-toplevel)) + ) + +(defmethod ede-html-documentation ((this ede-project)) + "Return a list of HTML files provided by project THIS." + + ) + +(defun ede-ecb-project-paths () + "Return a list of all paths for all active EDE projects. +This functions is meant for use with ECB." + (let ((p ede-projects) + (d nil)) + (while p + (setq d (cons (file-name-directory (oref (car p) file)) + d) + p (cdr p))) + d)) + +;;; EDE project-autoload methods +;; +(defmethod ede-dir-to-projectfile ((this ede-project-autoload) dir) + "Return a full file name of project THIS found in DIR. +Return nil if the project file does not exist." + (let* ((d (file-name-as-directory dir)) + (root (ede-project-root-directory this d)) + (pf (oref this proj-file)) + (f (cond ((stringp pf) + (expand-file-name pf (or root d))) + ((and (symbolp pf) (fboundp pf)) + (funcall pf (or root d))))) + ) + (when (and f (file-exists-p f)) + f))) + +;;; EDE basic functions +;; +(defun ede-add-project-to-global-list (proj) + "Add the project PROJ to the master list of projects. +On success, return the added project." + (when (not proj) + (error "No project created to add to master list")) + (when (not (eieio-object-p proj)) + (error "Attempt to add Non-object to master project list")) + (when (not (obj-of-class-p proj ede-project-placeholder)) + (error "Attempt to add a non-project to the ede projects list")) + (add-to-list 'ede-projects proj) + proj) + +(defun ede-load-project-file (dir &optional rootreturn) + "Project file independent way to read a project in from DIR. +Optional ROOTRETURN will return the root project for DIR." + ;; Only load if something new is going on. Flush the dirhash. + (ede-project-directory-remove-hash dir) + ;; Do the load + ;;(message "EDE LOAD : %S" file) + (let* ((file dir) + (path (expand-file-name (file-name-directory file))) + (pfc (ede-directory-project-p path)) + (toppath nil) + (o nil)) + (cond + ((not pfc) + ;; @TODO - Do we really need to scan? Is this a waste of time? + ;; Scan upward for a the next project file style. + (let ((p path)) + (while (and p (not (ede-directory-project-p p))) + (setq p (ede-up-directory p))) + (if p (ede-load-project-file p) + nil) + ;; recomment as we go + ;nil + )) + ;; Do nothing if we are buiding an EDE project already + (ede-constructing + nil) + ;; Load in the project in question. + (t + (setq toppath (ede-toplevel-project path)) + ;; We found the top-most directory. Check to see if we already + ;; have an object defining it's project. + (setq pfc (ede-directory-project-p toppath t)) + + ;; See if it's been loaded before + (setq o (object-assoc (ede-dir-to-projectfile pfc toppath) 'file + ede-projects)) + (if (not o) + ;; If not, get it now. + (let ((ede-constructing t)) + (setq o (funcall (oref pfc load-type) toppath)) + (when (not o) + (error "Project type error: :load-type failed to create a project")) + (ede-add-project-to-global-list o))) + + ;; Return the found root project. + (when rootreturn (set rootreturn o)) + + (let (tocheck found) + ;; Now find the project file belonging to FILE! + (setq tocheck (list o)) + (setq file (ede-dir-to-projectfile pfc (expand-file-name path))) + (while (and tocheck (not found)) + (let ((newbits nil)) + (when (car tocheck) + (if (string= file (oref (car tocheck) file)) + (setq found (car tocheck))) + (setq newbits (oref (car tocheck) subproj))) + (setq tocheck + (append (cdr tocheck) newbits)))) + (if (not found) + (message "No project for %s, but passes project-p test" file) + ;; Now that the file has been reset inside the project object, do + ;; the cache maintenance. + (setq ede-project-cache-files + (delete (oref found file) ede-project-cache-files))) + found))))) + +(defun ede-parent-project (&optional obj) + "Return the project belonging to the parent directory. +nil if there is no previous directory. +Optional argument OBJ is an object to find the parent of." + (let* ((proj (or obj ede-object-project)) ;; Current project. + (root (if obj (ede-project-root obj) + ede-object-root-project))) + ;; This case is a SHORTCUT if the project has defined + ;; a way to calculate the project root. + (if (and root proj (eq root proj)) + nil ;; we are at the root. + ;; Else, we may have a nil proj or root. + (let* ((thisdir (if obj (oref obj directory) + default-directory)) + (updir (ede-up-directory thisdir))) + (when updir + ;; If there was no root, perhaps we can derive it from + ;; updir now. + (let ((root (or root (ede-directory-get-toplevel-open-project updir)))) + (or + ;; This lets us find a subproject under root based on updir. + (and root + (ede-find-subproject-for-directory root updir)) + ;; Try the all structure based search. + (ede-directory-get-open-project updir) + ;; Load up the project file as a last resort. + ;; Last resort since it uses file-truename, and other + ;; slow features. + (and (ede-directory-project-p updir) + (ede-load-project-file + (file-name-as-directory updir)))))))))) + +(defun ede-current-project (&optional dir) + "Return the current project file. +If optional DIR is provided, get the project for DIR instead." + (let ((ans nil)) + ;; If it matches the current directory, do we have a pre-existing project? + (when (and (or (not dir) (string= dir default-directory)) + ede-object-project) + (setq ans ede-object-project) + ) + ;; No current project. + (when (not ans) + (let* ((ldir (or dir default-directory))) + (setq ans (ede-directory-get-open-project ldir)) + (or ans + ;; No open project, if this dir pass project-p, then load. + (when (ede-directory-project-p ldir) + (setq ans (ede-load-project-file ldir)))))) + ;; Return what we found. + ans)) + +(defun ede-buffer-object (&optional buffer) + "Return the target object for BUFFER. +This function clears cached values and recalculates." + (save-excursion + (if (not buffer) (setq buffer (current-buffer))) + (set-buffer buffer) + (setq ede-object nil) + (let ((po (ede-current-project))) + (if po (setq ede-object (ede-find-target po buffer)))) + (if (= (length ede-object) 1) + (setq ede-object (car ede-object))) + ede-object)) + +(defmethod ede-target-in-project-p ((proj ede-project) target) + "Is PROJ the parent of TARGET? +If TARGET belongs to a subproject, return that project file." + (if (and (slot-boundp proj 'targets) + (memq target (oref proj targets))) + proj + (let ((s (oref proj subproj)) + (ans nil)) + (while (and s (not ans)) + (setq ans (ede-target-in-project-p (car s) target)) + (setq s (cdr s))) + ans))) + +(defun ede-target-parent (target) + "Return the project which is the parent of TARGET. +It is recommended you track the project a different way as this function +could become slow in time." + ;; @todo - use ede-object-project as a starting point. + (let ((ans nil) (projs ede-projects)) + (while (and (not ans) projs) + (setq ans (ede-target-in-project-p (car projs) target) + projs (cdr projs))) + ans)) + +(defun ede-maybe-checkout (&optional buffer) + "Check BUFFER out of VC if necessary." + (save-excursion + (if buffer (set-buffer buffer)) + (if (and buffer-read-only vc-mode + (y-or-n-p "Checkout Makefile.am from VC? ")) + (vc-toggle-read-only)))) + +(defmethod ede-find-target ((proj ede-project) buffer) + "Fetch the target in PROJ belonging to BUFFER or nil." + (save-excursion + (set-buffer buffer) + (or ede-object + (if (ede-buffer-mine proj buffer) + proj + (let ((targets (oref proj targets)) + (f nil)) + (while targets + (if (ede-buffer-mine (car targets) buffer) + (setq f (cons (car targets) f))) + (setq targets (cdr targets))) + f))))) + +(defmethod ede-target-buffer-in-sourcelist ((this ede-target) buffer source) + "Return non-nil if object THIS is in BUFFER to a SOURCE list. +Handles complex path issues." + (member (ede-convert-path this (buffer-file-name buffer)) source)) + +(defmethod ede-buffer-mine ((this ede-project) buffer) + "Return non-nil if object THIS lays claim to the file in BUFFER." + nil) + +(defmethod ede-buffer-mine ((this ede-target) buffer) + "Return non-nil if object THIS lays claim to the file in BUFFER." + (condition-case nil + (ede-target-buffer-in-sourcelist this buffer (oref this source)) + ;; An error implies a bad match. + (error nil))) + + +;;; Project mapping +;; +(defun ede-project-buffers (project) + "Return a list of all active buffers controlled by PROJECT. +This includes buffers controlled by a specific target of PROJECT." + (let ((bl (buffer-list)) + (pl nil)) + (while bl + (save-excursion + (set-buffer (car bl)) + (if (and ede-object (eq (ede-current-project) project)) + (setq pl (cons (car bl) pl)))) + (setq bl (cdr bl))) + pl)) + +(defun ede-target-buffers (target) + "Return a list of buffers that are controlled by TARGET." + (let ((bl (buffer-list)) + (pl nil)) + (while bl + (save-excursion + (set-buffer (car bl)) + (if (if (listp ede-object) + (memq target ede-object) + (eq ede-object target)) + (setq pl (cons (car bl) pl)))) + (setq bl (cdr bl))) + pl)) + +(defun ede-buffers () + "Return a list of all buffers controled by an EDE object." + (let ((bl (buffer-list)) + (pl nil)) + (while bl + (save-excursion + (set-buffer (car bl)) + (if ede-object + (setq pl (cons (car bl) pl)))) + (setq bl (cdr bl))) + pl)) + +(defun ede-map-buffers (proc) + "Execute PROC on all buffers controled by EDE." + (mapcar proc (ede-buffers))) + +(defmethod ede-map-project-buffers ((this ede-project) proc) + "For THIS, execute PROC on all buffers belonging to THIS." + (mapcar proc (ede-project-buffers this))) + +(defmethod ede-map-target-buffers ((this ede-target) proc) + "For THIS, execute PROC on all buffers belonging to THIS." + (mapcar proc (ede-target-buffers this))) + +;; other types of mapping +(defmethod ede-map-subprojects ((this ede-project) proc) + "For object THIS, execute PROC on all direct subprojects. +This function does not apply PROC to sub-sub projects. +See also `ede-map-all-subprojects'." + (mapcar proc (oref this subproj))) + +(defmethod ede-map-all-subprojects ((this ede-project) allproc) + "For object THIS, execute PROC on THIS and all subprojects. +This function also applies PROC to sub-sub projects. +See also `ede-map-subprojects'." + (apply 'append + (list (funcall allproc this)) + (ede-map-subprojects + this + (lambda (sp) + (ede-map-all-subprojects sp allproc)) + ))) + +;; (ede-map-all-subprojects (ede-load-project-file "../semantic/") (lambda (sp) (oref sp file))) + +(defmethod ede-map-targets ((this ede-project) proc) + "For object THIS, execute PROC on all targets." + (mapcar proc (oref this targets))) + +(defmethod ede-map-any-target-p ((this ede-project) proc) + "For project THIS, map PROC to all targets and return if any non-nil. +Return the first non-nil value returned by PROC." + (ede-or (ede-map-targets this proc))) + + +;;; Some language specific methods. +;; +;; These items are needed by ede-cpp-root to add better support for +;; configuring items for Semantic. +(defun ede-apply-preprocessor-map () + "Apply preprocessor tables onto the current buffer." + (when (and ede-object (boundp 'semantic-lex-spp-macro-symbol-obarray)) + (let ((map (ede-preprocessor-map ede-object))) + (when map + ;; We can't do a require for the below symbol. + (setq semantic-lex-spp-macro-symbol-obarray + (semantic-lex-make-spp-table map)) + )))) + +(defmethod ede-system-include-path ((this ede-project)) + "Get the system include path used by project THIS." + nil) + +(defmethod ede-preprocessor-map ((this ede-project)) + "Get the pre-processor map for project THIS." + nil) + +(defmethod ede-system-include-path ((this ede-target)) + "Get the system include path used by project THIS." + nil) + +(defmethod ede-preprocessor-map ((this ede-target)) + "Get the pre-processor map for project THIS." + nil) + + +;;; Project-local variables +;; +(defun ede-make-project-local-variable (variable &optional project) + "Make VARIABLE project-local to PROJECT." + (if (not project) (setq project (ede-current-project))) + (if (assoc variable (oref project local-variables)) + nil + (oset project local-variables (cons (list variable) + (oref project local-variables))) + (mapcar (lambda (b) (save-excursion + (set-buffer b) + (make-local-variable variable))) + (ede-project-buffers project)))) + +(defmethod ede-set-project-variables ((project ede-project) &optional buffer) + "Set variables local to PROJECT in BUFFER." + (if (not buffer) (setq buffer (current-buffer))) + (save-excursion + (set-buffer buffer) + (mapcar (lambda (v) + (make-local-variable (car v)) + ;; set it's value here? + (set (car v) (cdr v)) + ) + (oref project local-variables)))) + +(defun ede-set (variable value &optional proj) + "Set the project local VARIABLE to VALUE. +If VARIABLE is not project local, just use set." + (let ((p (or proj (ede-current-project))) + a) + (if (and p (setq a (assoc variable (oref p local-variables)))) + (progn + (setcdr a value) + (mapc (lambda (b) (save-excursion + (set-buffer b) + (set variable value))) + (ede-project-buffers p))) + (set variable value)) + (ede-commit-local-variables p)) + value) + +(defmethod ede-commit-local-variables ((proj ede-project)) + "Commit change to local variables in PROJ." + nil) + + +;;; Accessors for more complex types where oref is inappropriate. +;; +(defmethod ede-target-sourcecode ((this ede-target)) + "Return the sourcecode objects which THIS permits." + (let ((sc (oref this sourcetype)) + (rs nil)) + (while (and (listp sc) sc) + (setq rs (cons (symbol-value (car sc)) rs) + sc (cdr sc))) + rs)) + + +;;; Lame stuff +;; +(defun ede-or (arg) + "Do `or' like stuff to ARG because you can't apply `or'." + (while (and arg (not (car arg))) + (setq arg (cdr arg))) + arg) + + +;;; Debugging. + +(defun ede-adebug-project () + "Run adebug against the current ede project. +Display the results as a debug list." + (interactive) + (require 'data-debug) + (when (ede-current-project) + (data-debug-new-buffer "*Analyzer ADEBUG*") + (data-debug-insert-object-slots (ede-current-project) "") + )) + +(defun ede-adebug-project-parent () + "Run adebug against the current ede parent project. +Display the results as a debug list." + (interactive) + (require 'data-debug) + (when (ede-parent-project) + (data-debug-new-buffer "*Analyzer ADEBUG*") + (data-debug-insert-object-slots (ede-parent-project) "") + )) + +(defun ede-adebug-project-root () + "Run adebug against the current ede parent project. +Display the results as a debug list." + (interactive) + (require 'data-debug) + (when (ede-toplevel) + (data-debug-new-buffer "*Analyzer ADEBUG*") + (data-debug-insert-object-slots (ede-toplevel) "") + )) + +;;; Hooks & Autoloads +;; +;; These let us watch various activities, and respond apropriatly. + +;; (add-hook 'edebug-setup-hook +;; (lambda () +;; (def-edebug-spec ede-with-projectfile +;; (form def-body)))) + +;; (autoload 'ede-update-version "ede-util" +;; "Update the version of the current project." t) + +;; (autoload 'ede-vc-project-directory "ede-system" t +;; "Run `vc-directory' on the the current project.") + +;; (autoload 'ede-web-browse-home "ede-system" t +;; "Web browse this project's home page.") + +;; (autoload 'ede-edit-web-page "ede-system" t +;; "Edit the web site for this project.") + +;; (autoload 'ede-upload-distribution "ede-system" t +;; "Upload the dist for this project to the upload site.") + +;; (autoload 'ede-upload-html-documentation "ede-system" t +;; "Upload auto-generated HTML to the web site.") + +(provide 'ede) + +;; Include this last because it depends on ede. +(require 'ede/files) + +;; If this does not occur after the provide, we can get a recursive +;; load. Yuck! +(if (featurep 'speedbar) + (ede-speedbar-file-setup) + (add-hook 'speedbar-load-hook 'ede-speedbar-file-setup)) + +;;; ede.el ends here diff --git a/lisp/cedet/ede/autoconf-edit.el b/lisp/cedet/ede/autoconf-edit.el new file mode 100644 index 00000000000..a59512da3e0 --- /dev/null +++ b/lisp/cedet/ede/autoconf-edit.el @@ -0,0 +1,424 @@ +;;; ede/autoconf-edit.el --- Keymap for autoconf + +;;; Copyright (C) 1998, 1999, 2000, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Keywords: project + +;; 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: +;; +;; Autoconf editing and modification support, and compatibility layer +;; for Emacses w/out autoconf mode built in. + +;;; Code: +(require 'autoconf) + +(defvar autoconf-new-automake-string + "dnl Process this file with autoconf to produce a configure script + +AC_INIT(%s) +AM_INIT_AUTOMAKE([%s], 0) +AM_CONFIG_HEADER(config.h) + +dnl End the configure script. +AC_OUTPUT(Makefile, [date > stamp-h] )\n" + "This string is used to initialize a new configure.in. +The default is designed to be used with automake. +The first %s will be filled with the test file. +The second %s will be filled with the program name.") + +(defun autoconf-new-program (rootdir program testfile) + "Initialize a new configure.in in ROOTDIR for PROGRAM using TESTFILE. +ROOTDIR is the root directory of a given autoconf controlled project. +PROGRAM is the program to be configured. +TESTFILE is the file used with AC_INIT. +configure the initial configure script using `autoconf-new-automake-string'" + (interactive "DRoot Dir: \nsProgram: \nsTest File: ") + (if (bufferp rootdir) + (set-buffer rootdir) + (let ((cf1 (expand-file-name "configure.in" rootdir)) + (cf2 (expand-file-name "configure.ac" rootdir))) + (if (and (or (file-exists-p cf1) (file-exists-p cf2)) + (not (y-or-n-p (format "File %s exists. Start Over? " + (if (file-exists-p cf1) + cf1 cf2) + )))) + (error "Quit")) + (find-file cf2))) + ;; Note, we only ask about overwrite if a string/path is specified. + (erase-buffer) + (insert (format autoconf-new-automake-string testfile program))) + +(defvar autoconf-preferred-macro-order + '("AC_INIT" + "AM_INIT_AUTOMAKE" + "AM_CONFIG_HEADER" + ;; Arg parsing + "AC_ARG_ENABLE" + "AC_ARG_WITH" + ;; Programs + "AC_PROG_MAKE_SET" + "AC_PROG_AWK" + "AC_PROG_CC" + "AC_PROG_CC_C_O" + "AC_PROG_CPP" + "AC_PROG_CXX" + "AC_PROG_CXXCPP" + "AC_ISC_POSIX" + "AC_PROG_F77" + "AC_PROG_GCC_TRADITIONAL" + "AC_PROG_INSTALL" + "AC_PROG_LEX" + "AC_PROG_LN_S" + "AC_PROG_RANLIB" + "AC_PROG_YACC" + "AC_CHECK_PROG" + "AC_CHECK_PROGS" + "AC_PROG_LIBTOOL" + ;; Libraries + "AC_CHECK_LIB" + "AC_PATH_XTRA" + ;; Headers + "AC_HEADER_STDC" + "AC_HEADER_SYS_WAIT" + "AC_HEADER_TIME" + "AC_HEADERS" + ;; Typedefs, structures + "AC_TYPE_PID_T" + "AC_TYPE_SIGNAL" + "AC_TYPE_UID_T" + "AC_STRUCT_TM" + ;; Compiler characteristics + "AC_CHECK_SIZEOF" + "AC_C_CONST" + ;; Library functions + "AC_CHECK_FUNCS" + "AC_TRY_LINK" + ;; System Services + ;; Other + "AM_PATH_LISPDIR" + "AM_INIT_GUILE_MODULE" + ;; AC_OUTPUT is always last + "AC_OUTPUT" + ) + "List of macros in the order that they prefer to occur in. +This helps when inserting a macro which doesn't yet exist +by positioning it near other macros which may exist. +From the autoconf manual: + `AC_INIT(FILE)' + checks for programs + checks for libraries + checks for header files + checks for typedefs + checks for structures + checks for compiler characteristics + checks for library functions + checks for system services + `AC_OUTPUT([FILE...])'") + +(defvar autoconf-multiple-macros + '("AC_ARG_ENABLE" + "AC_ARG_WITH" + "AC_CHECK_PROGS" + "AC_CHECK_LIB" + "AC_CHECK_SIZEOF" + "AC_TRY_LINK" + ) + "Macros which appear multiple times.") + +(defvar autoconf-multiple-multiple-macros + '("AC_HEADERS" "AC_CHECK_FUNCS") + "Macros which appear multiple times, and perform multiple queries.") + +(defun autoconf-in-macro (macro) + "Non-nil if point is in a macro of type MACRO." + (save-excursion + (beginning-of-line) + (looking-at (concat "\\(A[CM]_" macro "\\|" macro "\\)")))) + +(defun autoconf-find-last-macro (macro) + "Move to the last occurance of MACRO in FILE, and return that point. +The last macro is usually the one in which we would like to insert more +items such as CHECK_HEADERS." + (let ((op (point))) + (goto-char (point-max)) + (if (re-search-backward (concat "^" (regexp-quote macro) "\\s-*\\((\\|$\\)") nil t) + (progn + (beginning-of-line) + (point)) + (goto-char op) + nil))) + +(defun autoconf-parameter-strip (param) + "Strip the parameter PARAM of whitespace and misc characters." + (when (string-match "^\\s-*\\[?\\s-*" param) + (setq param (substring param (match-end 0)))) + (when (string-match "\\s-*\\]?\\s-*$" param) + (setq param (substring param 0 (match-beginning 0)))) + param) + +(defun autoconf-parameters-for-macro (macro) + "Retrieve the parameters to MACRO. +Returns a list of the arguments passed into MACRO as strings." + (save-excursion + (when (autoconf-find-last-macro macro) + (forward-sexp 1) + (mapcar + #'autoconf-parameter-strip + (when (looking-at "(") + (let* ((start (+ (point) 1)) + (end (save-excursion + (forward-sexp 1) + (- (point) 1))) + (ans (buffer-substring-no-properties start end))) + (split-string ans "," t))))))) + +(defun autoconf-position-for-macro (macro) + "Position the cursor where a new MACRO could be inserted. +This will appear at the BEGINNING of the macro MACRO should appear AFTER. +This is to make it compatible with `autoconf-find-last-macro'. +Assume that MACRO doesn't appear in the buffer yet, so search +the ordering list `autoconf-preferred-macro-order'." + ;; Search this list backwards.. heh heh heh + ;; This lets us do a reverse search easilly. + (let ((ml (member macro (reverse autoconf-preferred-macro-order)))) + (if (not ml) (error "Don't know how to position for %s yet" macro)) + (setq ml (cdr ml)) + (goto-char (point-max)) + (while (and ml (not (autoconf-find-last-macro (car ml)))) + (setq ml (cdr ml))) + (if (not ml) (error "Could not find context for positioning %s" macro)))) + +(defun autoconf-insert-macro-at-point (macro &optional param) + "Add MACRO at the current point with PARAM." + (insert macro) + (if param + (progn + (insert "(" param ")") + (if (< (current-column) 3) (insert " dnl"))))) + +(defun autoconf-insert-new-macro (macro &optional param) + "Add a call to MACRO in the current autoconf file. +Deals with macro order. See `autoconf-preferred-macro-order' and +`autoconf-multi-macros'. +Optional argument PARAM is the parameter to pass to the macro as one string." + (cond ((member macro autoconf-multiple-macros) + ;; This occurs multiple times + (or (autoconf-find-last-macro macro) + (autoconf-position-for-macro macro)) + (forward-sexp 2) + (end-of-line) + (insert "\n") + (autoconf-insert-macro-at-point macro param)) + ((member macro autoconf-multiple-multiple-macros) + (if (not param) + (error "You must have a paramter for %s" macro)) + (if (not (autoconf-find-last-macro macro)) + (progn + ;; Doesn't exist yet.... + (autoconf-position-for-macro macro) + (forward-sexp 2) + (end-of-line) + (insert "\n") + (autoconf-insert-macro-at-point macro param)) + ;; Does exist, can we fit onto the current line? + (forward-sexp 2) + (down-list -1) + (if (> (+ (current-column) (length param)) fill-column) + (insert " " param) + (up-list 1) + (end-of-line) + (insert "\n") + (autoconf-insert-macro-at-point macro param)))) + ((autoconf-find-last-macro macro) + ;; If it isn't one of the multi's, it's a singleton. + ;; If it exists, ignore it. + nil) + (t + (autoconf-position-for-macro macro) + (forward-sexp 1) + (if (looking-at "\\s-*(") + (forward-sexp 1)) + (end-of-line) + (insert "\n") + (autoconf-insert-macro-at-point macro param)))) + +(defun autoconf-find-query-for-header (header) + "Position the cursor where HEADER is queried." + (interactive "sHeader: ") + (let ((op (point)) + (found t)) + (goto-char (point-min)) + (condition-case nil + (while (not + (progn + (re-search-forward + (concat "\\b" (regexp-quote header) "\\b")) + (save-excursion + (beginning-of-line) + (looking-at "AC_CHECK_HEADERS"))))) + ;; We depend on the search failing to exit our loop on failure. + (error (setq found nil))) + (if (not found) (goto-char op)) + found)) + +(defun autoconf-add-query-for-header (header) + "Add in HEADER to be queried for in our autoconf file." + (interactive "sHeader: ") + (or (autoconf-find-query-for-header header) + (autoconf-insert-new-macro "AC_CHECK_HEADERS" header))) + + +(defun autoconf-find-query-for-func (func) + "Position the cursor where FUNC is queried." + (interactive "sFunction: ") + (let ((op (point)) + (found t)) + (goto-char (point-min)) + (condition-case nil + (while (not + (progn + (re-search-forward + (concat "\\b" (regexp-quote func) "\\b")) + (save-excursion + (beginning-of-line) + (looking-at "AC_CHECK_FUNCS"))))) + ;; We depend on the search failing to exit our loop on failure. + (error (setq found nil))) + (if (not found) (goto-char op)) + found)) + +(defun autoconf-add-query-for-func (func) + "Add in FUNC to be queried for in our autoconf file." + (interactive "sFunction: ") + (or (autoconf-find-query-for-func func) + (autoconf-insert-new-macro "AC_CHECK_FUNCS" func))) + +(defvar autoconf-program-builtin + '(("AWK" . "AC_PROG_AWK") + ("CC" . "AC_PROG_CC") + ("CPP" . "AC_PROG_CPP") + ("CXX" . "AC_PROG_CXX") + ("CXXCPP" . "AC_PROG_CXXCPP") + ("F77" . "AC_PROG_F77") + ("GCC_TRADITIONAL" . "AC_PROG_GCC_TRADITIONAL") + ("INSTALL" . "AC_PROG_INSTALL") + ("LEX" . "AC_PROG_LEX") + ("LN_S" . "AC_PROG_LN_S") + ("RANLIB" . "AC_PROG_RANLIB") + ("YACC" . "AC_PROG_YACC") + ) + "Association list of PROGRAM variables and their built-in MACRO.") + +(defun autoconf-find-query-for-program (prog) + "Position the cursor where PROG is queried. +PROG is the VARIABLE to use in autoconf to identify the program. +PROG excludes the _PROG suffix. Thus if PROG were EMACS, then the +variable in configure.in would be EMACS_PROG." + (let ((op (point)) + (found t) + (builtin (assoc prog autoconf-program-builtin))) + (goto-char (point-min)) + (condition-case nil + (re-search-forward + (concat "^" + (or (cdr-safe builtin) + (concat "AC_CHECK_PROG\\s-*(\\s-*" prog "_PROG")) + "\\>")) + (error (setq found nil))) + (if (not found) (goto-char op)) + found)) + +(defun autoconf-add-query-for-program (prog &optional names) + "Add in PROG to be queried for in our autoconf file. +Optional NAMES is for non-built-in programs, and is the list +of possible names." + (interactive "sProgram: ") + (if (autoconf-find-query-for-program prog) + nil + (let ((builtin (assoc prog autoconf-program-builtin))) + (if builtin + (autoconf-insert-new-macro (cdr builtin)) + ;; Not built in, try the params item + (autoconf-insert-new-macro "AC_CHECK_PROGS" (concat prog "," names)) + )))) + +;;; Scrappy little changes +;; +(defvar autoconf-deleted-text nil + "Set to the last bit of text deleted during an edit.") + +(defvar autoconf-inserted-text nil + "Set to the last bit of text inserted during an edit.") + +(defmacro autoconf-edit-cycle (&rest body) + "Start an edit cycle, unsetting the modified flag if there is no change. +Optional argument BODY is the code to execute which edits the autoconf file." + `(let ((autoconf-deleted-text nil) + (autoconf-inserted-text nil) + (mod (buffer-modified-p))) + ,@body + (if (and (not mod) + (string= autoconf-deleted-text autoconf-inserted-text)) + (set-buffer-modified-p nil)))) + +(defun autoconf-delete-parameter (index) + "Delete the INDEXth parameter from the macro starting on the current line. +Leaves the cursor where a new parameter can be inserted. +INDEX starts at 1." + (beginning-of-line) + (down-list 1) + (re-search-forward ", ?" nil nil (1- index)) + (let ((end (save-excursion + (re-search-forward ",\\|)" (save-excursion + (end-of-line) + (point))) + (forward-char -1) + (point)))) + (setq autoconf-deleted-text (buffer-substring (point) end)) + (delete-region (point) end))) + +(defun autoconf-insert (text) + "Insert TEXT." + (setq autoconf-inserted-text text) + (insert text)) + +(defun autoconf-set-version (version) + "Set the version used with automake to VERSION." + (if (not (stringp version)) + (signal 'wrong-type-argument '(stringp version))) + (if (not (autoconf-find-last-macro "AM_INIT_AUTOMAKE")) + (error "Cannot update version") + ;; Move to correct position. + (autoconf-edit-cycle + (autoconf-delete-parameter 2) + (autoconf-insert version)))) + +(defun autoconf-set-output (outputlist) + "Set the files created in AC_OUTPUT to OUTPUTLIST. +OUTPUTLIST is a list of strings representing relative paths +to Makefiles, or other files using Autoconf substitution." + (if (not (autoconf-find-last-macro "AC_OUTPUT")) + (error "Cannot update version") + (autoconf-edit-cycle + (autoconf-delete-parameter 1) + (autoconf-insert (mapconcat (lambda (a) a) outputlist " "))))) + +(provide 'ede/autoconf-edit) + +;;; ede/autoconf-edit.el ends here diff --git a/lisp/cedet/ede/cpp-root.el b/lisp/cedet/ede/cpp-root.el new file mode 100644 index 00000000000..02f86d2c856 --- /dev/null +++ b/lisp/cedet/ede/cpp-root.el @@ -0,0 +1,515 @@ +;;; ede/cpp-root.el --- A simple way to wrap a C++ project with a single root + +;; Copyright (C) 2007, 2008, 2009 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: +;; +;; NOTE: ede-cpp-root.el has been commented so as to also make it +;; useful for learning how to make similar project types. +;; +;; Not everyone can use automake, or an EDE project type. For +;; pre-existing code, it is often helpful jut to be able to wrap the +;; whole thing up in as simple a way as possible. +;; +;; The cpp-root project type will allow you to create a single object +;; with no save-file in your .emacs file that will be recognized, and +;; provide a way to easilly allow EDE to provide Semantic with the +;; ability to find header files, and other various source files +;; quickly. +;; +;; The cpp-root class knows a few things about C++ projects, such as +;; the prevalence of "include" directories, and typical file-layout +;; stuff. If this isn't sufficient, you can subclass +;; `ede-cpp-root-project' and add your own tweaks in just a few lines. +;; See the end of this file for an example. +;; +;;; EXAMPLE +;; +;; Add this to your .emacs file, modifying apropriate bits as needed. +;; +;; (ede-cpp-root-project "SOMENAME" :file "/dir/to/some/file") +;; +;; Replace SOMENAME with whatever name you want, and the filename to +;; an actual file at the root of your project. It might be a +;; Makefile, a README file. Whatever. It doesn't matter. It's just +;; a key to hang the rest of EDE off of. +;; +;; The most likely reason to create this project, is to help make +;; finding files within the project faster. In conjunction with +;; Semantic completion, having a short include path is key. You can +;; override the include path like this: +;; +;; (ede-cpp-root-project "NAME" :file "FILENAME" +;; :include-path '( "/include" "../include" "/c/include" ) +;; :system-include-path '( "/usr/include/c++/3.2.2/" ) +;; :spp-table '( ("MOOSE" . "") +;; ("CONST" . "const") ) +;; :spp-files '( "include/config.h" ) +;; ) +;; +;; In this case each item in the include path list is searched. If +;; the directory starts with "/", then that expands to the project +;; root directory. If a directory does not start with "/", then it +;; is relative to the default-directory of the current buffer when +;; the file name is expanded. +;; +;; The include path only affects C/C++ header files. Use the slot +;; :header-match-regexp to change it. +;; +;; The :system-include-path allows you to specify full directory +;; names to include directories where system header files can be +;; found. These will be applied to files in this project only. +;; +;; The :spp-table provides a list of project specific #define style +;; macros that are unique to this project, passed in to the compiler +;; on the command line, or are in special headers. +;; +;; The :spp-files option is like :spp-table, except you can provide a +;; file name for a header in your project where most of your CPP +;; macros reside. Doing this can be easier than listing everything in +;; the :spp-table option. The files listed in :spp-files should not +;; start with a /, and are relative to something in :include-path.;; +;; +;; If you want to override the file-finding tool with your own +;; function you can do this: +;; +;; (ede-cpp-root-project "NAME" :file "FILENAME" :locate-fcn 'MYFCN) +;; +;; Where FILENAME is a file in the root directory of the project. +;; Where MYFCN is a symbol for a function. See: +;; +;; M-x describe-class RET ede-cpp-root-project RET +;; +;; for documentation about the locate-fcn extension. +;; +;;; ADVANCED EXAMPLE +;; +;; If the cpp-root project style is right for you, but you want a +;; dynamic loader, instead of hard-coding values in your .emacs, you +;; can do that too, but you will need to write some lisp code. +;; +;; To do that, you need to add an entry to the +;; `ede-project-class-files' list, and also provide two functions to +;; teach EDE how to load your project pattern +;; +;; It would oook like this: +;; +;; (defun MY-FILE-FOR-DIR (&optional dir) +;; "Return a full file name to the project file stored in DIR." +;; <write your code here, or return nil> +;; ) +;; +;; (defun MY-ROOT-FCN () +;; "Return the root directory for `default-directory'" +;; ;; You might be able to use `ede-cpp-root-project-root'. +;; ) +;; +;; (defun MY-LOAD (dir) +;; "Load a project of type `cpp-root' for the directory DIR. +;; Return nil if there isn't one." +;; (ede-cpp-root-project "NAME" :file (expand-file-name "FILE" dir) +;; :locate-fcn 'MYFCN) +;; ) +;; +;; (add-to-list 'ede-project-class-files +;; (ede-project-autoload "cpp-root" +;; :name "CPP ROOT" +;; :file 'ede-cpp-root +;; :proj-file 'MY-FILE-FOR-DIR +;; :proj-root 'MY-ROOT-FCN +;; :load-type 'MY-LOAD +;; :class-sym 'ede-cpp-root) +;; t) +;; +;;; TODO +;; +;; Need a way to reconfigure a project, and have it affect all open buffers. +;; From Tobias Gerdin: +;; +;; >>3) Is there any way to refresh a ede-cpp-root-project dynamically? I have +;; >>some file open part of the project, fiddle with the include paths and would +;; >>like the open buffer to notice this when I re-evaluate the +;; >>ede-cpp-root-project constructor. +;; > +;; > Another good idea. The easy way is to "revert-buffer" as needed. The +;; > ede "project local variables" does this already, so it should be easy +;; > to adapt something. +;; +;; I actually tried reverting the buffer but Semantic did not seem to pick +;; up the differences (the "include summary" reported the same include paths). + +(require 'ede) + +(defvar semantic-lex-spp-project-macro-symbol-obarray) +(declare-function semantic-lex-make-spp-table "semantic/lex-spp") +(declare-function semanticdb-file-table-object "semantic/db") +(declare-function semanticdb-needs-refresh-p "semantic/db") +(declare-function semanticdb-refresh-table "semantic/db") + +;;; Code: + +;;; PROJECT CACHE: +;; +;; cpp-root projects are created in a .emacs or other config file, but +;; there still needs to be a way for a particular file to be +;; identified against it. The cache is where we look to map a file +;; against a project. +;; +;; Setting up a simple in-memory cache of active projects allows the +;; user to re-load their configuration file several times without +;; messing up the active project set. +;; +(defvar ede-cpp-root-project-list nil + "List of projects created by option `ede-cpp-root-project'.") + +(defun ede-cpp-root-file-existing (dir) + "Find a cpp-root project in the list of cpp-root projects. +DIR is the directory to search from." + (let ((projs ede-cpp-root-project-list) + (ans nil)) + (while (and projs (not ans)) + (let ((root (ede-project-root-directory (car projs)))) + (when (string-match (concat "^" (regexp-quote root)) dir) + (setq ans (car projs)))) + (setq projs (cdr projs))) + ans)) + +;;; PROJECT AUTOLOAD CONFIG +;; +;; Each project type registers itself into the project-class list. +;; This way, each time a file is loaded, EDE can map that file to a +;; project. This project type checks files against the internal cache +;; of projects created by the user. +;; +;; EDE asks two kinds of questions. One is, does this DIR belong to a +;; project. If it does, it then asks, what is the ROOT directory to +;; the project in DIR. This is easy for cpp-root projects, but more +;; complex for multiply nested projects. +;; +;; If EDE finds out that a project exists for DIR, it then loads that +;; project. The LOAD routine can either create a new project object +;; (if it needs to load it off disk) or more likely can return an +;; existing object for the discovered directory. cpp-root always uses +;; the second case. + +(defun ede-cpp-root-project-file-for-dir (&optional dir) + "Return a full file name to the project file stored in DIR." + (let ((proj (ede-cpp-root-file-existing dir))) + (when proj (oref proj :file)))) + +(defvar ede-cpp-root-count 0 + "Count number of hits to the cpp root thing. +This is a debugging variable to test various optimizations in file +lookup in the main EDE logic.") + +;;;###autoload +(defun ede-cpp-root-project-root (&optional dir) + "Get the root directory for DIR." + (let ((projfile (ede-cpp-root-project-file-for-dir + (or dir default-directory)))) + (setq ede-cpp-root-count (1+ ede-cpp-root-count)) + ;(debug) + (when projfile + (file-name-directory projfile)))) + +(defun ede-cpp-root-load (dir &optional rootproj) + "Return a CPP root object if you created one. +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." + ;; Snoop through our master list. + (ede-cpp-root-file-existing dir)) + +;;; CLASSES +;; +;; EDE sets up projects with two kinds of objects. +;; +;; The PROJECT is a class that represents everything under a directory +;; hierarchy. A TARGET represents a subset of files within a project. +;; A project can have multiple targets, and multiple sub-projects. +;; Sub projects should map to sub-directories. +;; +;; The CPP-ROOT project maps any file in C or C++ mode to a target for +;; C files. +;; +;; When creating a custom project the project developer an opportunity +;; to run code to setup various tools whenever an associated buffer is +;; loaded. The CPP-ROOT project spends most of its time setting up C +;; level include paths, and PreProcessor macro tables. + +(defclass ede-cpp-root-target (ede-target) + () + "EDE cpp-root project target. +All directories need at least one target.") + +(defclass ede-cpp-root-project (ede-project eieio-instance-tracker) + ((tracking-symbol :initform 'ede-cpp-root-project-list) + (include-path :initarg :include-path + :initform '( "/include" "../include/" ) + :type list + :documentation + "The default locate function expands filenames within a project. +If a header file (.h, .hh, etc) name is expanded, and +the :locate-fcn slot is nil, then the include path is checked +first, and other directories are ignored. For very large +projects, this optimization can save a lot of time. + +Directory names in the path can be relative to the current +buffer's `default-directory' (not starting with a /). Directories +that are relative to the project's root should start with a /, such +as \"/include\", meaning the directory `include' off the project root +directory.") + (system-include-path :initarg :system-include-path + :initform nil + :type list + :documentation + "The system include path for files in this project. +C files initialized in an ede-cpp-root-project have their semantic +system include path set to this value. If this is nil, then the +semantic path is not modified.") + (spp-table :initarg :spp-table + :initform nil + :type list + :documentation + "C Preprocessor macros for your files. +Preprocessor symbols will be used while parsing your files. +These macros might be passed in through the command line compiler, or +are critical symbols derived from header files. Providing header files +macro values through this slot improves accuracy and performance. +Use `:spp-files' to use these files directly.") + (spp-files :initarg :spp-files + :initform nil + :type list + :documentation + "C header file with Preprocessor macros for your files. +The PreProcessor symbols appearing in these files will be used while +parsing files in this project. +See `semantic-lex-c-preprocessor-symbol-map' for more on how this works.") + (header-match-regexp :initarg :header-match-regexp + :initform + "\\.\\(h\\(h\\|xx\\|pp\\|\\+\\+\\)?\\|H\\)$\\|\\<\\w+$" + :type string + :documentation + "Regexp used to identify C/C++ header files.") + (locate-fcn :initarg :locate-fcn + :initform nil + :type (or null function) + :documentation + "The locate function can be used in place of +`ede-expand-filename' so you can quickly customize your custom target +to use specialized local routines instead of the EDE routines. +The function symbol must take two arguments: + NAME - The name of the file to find. + DIR - The directory root for this cpp-root project. + +It should return the fully qualified file name passed in from NAME. If that file does not +exist, it should return nil." + ) + ) + "EDE cpp-root project class. +Each directory needs a a project file to control it.") + +;;; INIT +;; +;; Most projects use `initialize-instance' to do special setup +;; on the object when it is created. In this case, EDE-CPP-ROOT can +;; find previous copies of this project, and make sure that one of the +;; objects is deleted. + +(defmethod initialize-instance ((this ede-cpp-root-project) + &rest fields) + "Make sure the :file is fully expanded." + ;; Add ourselves to the master list + (call-next-method) + (let ((f (expand-file-name (oref this :file)))) + ;; Remove any previous entries from the main list. + (let ((old (eieio-instance-tracker-find (file-name-directory f) + :directory 'ede-cpp-root-project-list))) + ;; This is safe, because :directory isn't filled in till later. + (when (and old (not (eq old this))) + (delete-instance old))) + ;; Basic initialization. + (when (or (not (file-exists-p f)) + (file-directory-p f)) + (delete-instance this) + (error ":file for ede-cpp-root must be a file.")) + (oset this :file f) + (oset this :directory (file-name-directory f)) + (ede-project-directory-remove-hash (file-name-directory f)) + (ede-add-project-to-global-list this) + (unless (slot-boundp this 'targets) + (oset this :targets nil)) + ;; We need to add ourselves to the master list. + ;;(setq ede-projects (cons this ede-projects)) + )) + +;;; SUBPROJ Management. +;; +;; This is a way to allow a subdirectory to point back to the root +;; project, simplifying authoring new single-point projects. + +(defmethod ede-find-subproject-for-directory ((proj ede-cpp-root-project) + dir) + "Return PROJ, for handling all subdirs below DIR." + proj) + +;;; TARGET MANAGEMENT +;; +;; Creating new targets on a per directory basis is a good way to keep +;; files organized. See ede-emacs for an example with multiple file +;; types. +(defmethod ede-find-target ((proj ede-cpp-root-project) buffer) + "Find an EDE target in PROJ for BUFFER. +If one doesn't exist, create a new one for this directory." + (let* ((targets (oref proj targets)) + (dir default-directory) + (ans (object-assoc dir :path targets)) + ) + (when (not ans) + (setq ans (ede-cpp-root-target dir + :name (file-name-nondirectory + (directory-file-name dir)) + :path dir + :source nil)) + (object-add-to-list proj :targets ans) + ) + ans)) + +;;; FILE NAMES +;; +;; One of the more important jobs of EDE is to find files in a +;; directory structure. cpp-root has tricks it knows about how most C +;; projects are set up with include paths. +;; +;; This tools also uses the ede-locate setup for augmented file name +;; lookup using external tools. +(defmethod ede-expand-filename-impl ((proj ede-cpp-root-project) name) + "Within this project PROJ, find the file NAME. +This knows details about or source tree." + ;; The slow part of the original is looping over subprojects. + ;; This version has no subprojects, so this will handle some + ;; basic cases. + (let ((ans (call-next-method))) + (unless ans + (let* ((lf (oref proj locate-fcn)) + (dir (file-name-directory (oref proj file)))) + (if lf + (setq ans (funcall lf name dir)) + (if (ede-cpp-root-header-file-p proj name) + ;; Else, use our little hack. + (let ((ip (oref proj include-path)) + (tmp nil)) + (while ip + ;; Translate + (setq tmp (ede-cpp-root-translate-file proj (car ip))) + ;; Test this name. + (setq tmp (expand-file-name name tmp)) + (if (file-exists-p tmp) + (setq ans tmp)) + (setq ip (cdr ip)) )) + ;; Else, do the usual. + (setq ans (call-next-method))) + ))) + (or ans (call-next-method)))) + +(defmethod ede-project-root ((this ede-cpp-root-project)) + "Return my root." + this) + +(defmethod ede-project-root-directory ((this ede-cpp-root-project)) + "Return my root." + (file-name-directory (oref this file))) + +;;; C/CPP SPECIFIC CODE +;; +;; The following code is specific to setting up header files, +;; include lists, and Preprocessor symbol tables. + +(defmethod ede-cpp-root-header-file-p ((proj ede-cpp-root-project) name) + "Non nil if in PROJ the filename NAME is a header." + (save-match-data + (string-match (oref proj header-match-regexp) name))) + +(defmethod ede-cpp-root-translate-file ((proj ede-cpp-root-project) filename) + "For PROJ, translate a user specified FILENAME. +This is for project include paths and spp source files." + ;; Step one: Root of this project. + (let ((dir (file-name-directory (oref proj file)))) + + ;; Step two: Analyze first char, and rehost + (if (and (not (string= filename "")) (= (aref filename 0) ?/)) + ;; Check relative to root of project + (setq filename (expand-file-name (substring filename 1) + dir)) + ;; Relative to current directory. + (setq filename (expand-file-name filename))) + + filename)) + +(defmethod ede-set-project-variables ((project ede-cpp-root-project) &optional buffer) + "Set variables local to PROJECT in BUFFER. +Also set up the lexical preprocessor map." + (call-next-method) + (when (and (featurep 'semantic-c) (featurep 'semantic-lex-spp)) + (setq semantic-lex-spp-project-macro-symbol-obarray + (semantic-lex-make-spp-table (oref project spp-table))) + )) + +(defmethod ede-system-include-path ((this ede-cpp-root-project)) + "Get the system include path used by project THIS." + (oref this system-include-path)) + +(defmethod ede-preprocessor-map ((this ede-cpp-root-project)) + "Get the pre-processor map for project THIS." + (require 'semantic/db) + (let ((spp (oref this spp-table)) + (root (ede-project-root this)) + ) + (mapc + (lambda (F) + (let* ((expfile (ede-expand-filename root F)) + (table (when expfile + (semanticdb-file-table-object expfile))) + ) + (when (not table) + (message "Cannot find file %s in project." F)) + (when (and table (semanticdb-needs-refresh-p table)) + (semanticdb-refresh-table table)) + (setq spp (append spp (oref table lexical-table))))) + (oref this spp-files)) + spp)) + +(defmethod ede-system-include-path ((this ede-cpp-root-target)) + "Get the system include path used by project THIS." + (ede-system-include-path (ede-target-parent this))) + +(defmethod ede-preprocessor-map ((this ede-cpp-root-target)) + "Get the pre-processor map for project THIS." + (ede-preprocessor-map (ede-target-parent this))) + +(provide 'ede/cpp-root) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: ede/loaddefs +;; generated-autoload-load-name: "ede/cpp-root" +;; End: + +;;; ede/cpp-root.el ends here diff --git a/lisp/cedet/ede/dired.el b/lisp/cedet/ede/dired.el new file mode 100644 index 00000000000..6be3750297c --- /dev/null +++ b/lisp/cedet/ede/dired.el @@ -0,0 +1,109 @@ +;;; ede/dired.el --- EDE extensions to dired. + +;;; Copyright (C) 1998, 1999, 2000, 2003 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Version: 0.4 +;; 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 provides a dired interface to EDE, allowing users to modify +;; their project file by adding files (or whatever) directly from a +;; dired buffer. + +(require 'easymenu) +(require 'dired) +(require 'ede) + +;;; Code: +(defvar ede-dired-minor-mode nil + "Non-nil when in ede dired minor mode.") +(make-variable-buffer-local 'ede-dired-minor-mode) + +(defvar ede-dired-keymap nil + "Keymap used for ede dired minor mode.") + +(if ede-dired-keymap + nil + (setq ede-dired-keymap (make-sparse-keymap)) + (define-key ede-dired-keymap ".a" 'ede-dired-add-to-target) + (define-key ede-dired-keymap ".t" 'ede-new-target) + (define-key ede-dired-keymap ".s" 'ede-speedbar) + (define-key ede-dired-keymap ".C" 'ede-compile-project) + (define-key ede-dired-keymap ".d" 'ede-make-dist) + + (easy-menu-define + ede-dired-menu ede-dired-keymap "EDE Dired Minor Mode Menu" + '("Project" + [ "Add files to target" ede-dired-add-to-target (ede-current-project) ] + ( "Build" :filter ede-build-forms-menu) + "-" + [ "Create Project" ede-new (not (ede-current-project)) ] + [ "Create Target" ede-new-target (ede-current-project) ] + "-" + ( "Customize Project" :filter ede-customize-forms-menu ) + [ "View Project Tree" ede-speedbar (ede-current-project) ] + )) + ) + +(defun ede-dired-minor-mode (&optional arg) + "A minor mode that should only be activated in DIRED buffers. +If ARG is nil, toggle, if it is a positive number, force on, if +negative, force off." + (interactive "P") + (if (not (or (eq major-mode 'dired-mode) + (eq major-mode 'vc-dired-mode))) + (error "Not in DIRED mode")) + (setq ede-dired-minor-mode + (not (or (and (null arg) ede-dired-minor-mode) + (<= (prefix-numeric-value arg) 0)))) + (if (and (not (ede-directory-project-p default-directory)) + (not (interactive-p))) + (setq ede-dired-minor-mode nil)) + ) + +(defun ede-dired-add-to-target (target) + "Add a file, or all marked files into a TARGET." + (interactive (list + (let ((ede-object (ede-current-project))) + (ede-invoke-method 'project-interactive-select-target + "Add files to Target: ")))) + (let ((files (dired-get-marked-files t))) + (while files + (project-add-file target (car files)) + ;; Find the buffer for this files, and set it's ede-object + (if (get-file-buffer (car files)) + (save-excursion + (set-buffer (get-file-buffer (car files))) + (setq ede-object nil) + (setq ede-object (ede-buffer-object (current-buffer))))) + ;; Increment. + (setq files (cdr files))))) + +;; Minor mode management. +(add-to-list 'minor-mode-alist '(ede-dired-minor-mode " EDE")) +(let ((a (assoc 'ede-dired-minor-mode minor-mode-map-alist))) + (if a + (setcdr a ede-dired-keymap) + (add-to-list 'minor-mode-map-alist (cons 'ede-dired-minor-mode + ede-dired-keymap)))) + +(provide 'ede/dired) + +;;; ede/dired.el ends here diff --git a/lisp/cedet/ede/emacs.el b/lisp/cedet/ede/emacs.el new file mode 100644 index 00000000000..7f071708f3f --- /dev/null +++ b/lisp/cedet/ede/emacs.el @@ -0,0 +1,257 @@ +;;; ede/emacs.el --- Special project for Emacs + +;; Copyright (C) 2008, 2009 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: +;; +;; Provide a special project type just for Emacs, cause Emacs is special. +;; +;; Identifies an Emacs project automatically. +;; Speedy ede-expand-filename based on extension. +;; Pre-populates the preprocessor map from lisp.h +;; +;; ToDo : +;; * Add "build" options. +;; * Add texinfo lookup options. +;; * Add website + +(require 'ede) +(declare-function semanticdb-file-table-object "semantic/db") +(declare-function semanticdb-needs-refresh-p "semantic/db") +(declare-function semanticdb-refresh-table "semantic/db") + +;;; Code: +(defvar ede-emacs-project-list nil + "List of projects created by option `ede-emacs-project'.") + +(defun ede-emacs-file-existing (dir) + "Find a Emacs project in the list of Emacs projects. +DIR is the directory to search from." + (let ((projs ede-emacs-project-list) + (ans nil)) + (while (and projs (not ans)) + (let ((root (ede-project-root-directory (car projs)))) + (when (string-match (concat "^" (regexp-quote root)) dir) + (setq ans (car projs)))) + (setq projs (cdr projs))) + ans)) + +;;;###autoload +(defun ede-emacs-project-root (&optional dir) + "Get the root directory for DIR." + (when (not dir) (setq dir default-directory)) + (let ((case-fold-search t) + (proj (ede-emacs-file-existing dir))) + (if proj + (ede-up-directory (file-name-directory + (oref proj :file))) + ;; No pre-existing project. Lets take a wild-guess if we have + ;; an Emacs project here. + (when (string-match "emacs[^/]*" dir) + (let ((base (substring dir 0 (match-end 0)))) + (when (file-exists-p (expand-file-name "src/emacs.c" base)) + base)))))) + +(defun ede-emacs-version (dir) + "Find the Emacs version for the Emacs src in DIR." + (let ((buff (get-buffer-create " *emacs-query*"))) + (save-excursion + (set-buffer buff) + (erase-buffer) + (setq default-directory (file-name-as-directory dir)) + (call-process "egrep" nil buff nil "-n" "-e" "^version=" "Makefile") + (goto-char (point-min)) + (re-search-forward "version=\\([0-9.]+\\)") + (prog1 + (match-string 1) + (kill-buffer buff) + )))) + +(defclass ede-emacs-project (ede-project eieio-instance-tracker) + ((tracking-symbol :initform 'ede-emacs-project-list) + ) + "Project Type for the Emacs source code." + :method-invocation-order :depth-first) + +(defun ede-emacs-load (dir &optional rootproj) + "Return an Emacs 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." + (or (ede-emacs-file-existing dir) + ;; Doesn't already exist, so lets make one. + (ede-emacs-project "Emacs" + :name (concat "Emacs" (ede-emacs-version dir)) + :directory dir + :file (expand-file-name "src/emacs.c" + dir)) + (ede-add-project-to-global-list this) + ) + ) + +(defclass ede-emacs-target-c (ede-target) + () + "EDE Emacs Project target for C code. +All directories need at least one target.") + +(defclass ede-emacs-target-el (ede-target) + () + "EDE Emacs Project target for Emacs Lisp code. +All directories need at least one target.") + +(defclass ede-emacs-target-misc (ede-target) + () + "EDE Emacs Project target for Misc files. +All directories need at least one target.") + +(defmethod initialize-instance ((this ede-emacs-project) + &rest fields) + "Make sure the :file is fully expanded." + (call-next-method) + (unless (slot-boundp this 'targets) + (oset this :targets nil))) + +;;; File Stuff +;; +(defmethod ede-project-root-directory ((this ede-emacs-project) + &optional file) + "Return the root for THIS Emacs project with file." + (ede-up-directory (file-name-directory (oref this file)))) + +(defmethod ede-project-root ((this ede-emacs-project)) + "Return my root." + this) + +(defmethod ede-find-subproject-for-directory ((proj ede-emacs-project) + dir) + "Return PROJ, for handling all subdirs below DIR." + proj) + +;;; TARGET MANAGEMENT +;; +(defun ede-emacs-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-emacs-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))) + (cls (cond ((not ext) + 'ede-emacs-target-misc) + ((string-match "c\\|h" ext) + 'ede-emacs-target-c) + ((string-match "elc?" ext) + 'ede-emacs-target-el) + (t 'ede-emacs-target-misc))) + (targets (oref proj targets)) + (dir default-directory) + (ans (ede-emacs-find-matching-target cls dir targets)) + ) + (when (not ans) + (setq ans (make-instance + cls + :name (file-name-nondirectory + (directory-file-name dir)) + :path dir + :source nil)) + (object-add-to-list proj :targets ans) + ) + ans)) + +;;; UTILITIES SUPPORT. +;; +(defmethod ede-preprocessor-map ((this ede-emacs-target-c)) + "Get the pre-processor map for Emacs C code. +All files need the macros from lisp.h!" + (require 'semantic/db) + (let* ((proj (ede-target-parent this)) + (root (ede-project-root proj)) + (table (semanticdb-file-table-object + (ede-expand-filename root "lisp.h"))) + filemap + ) + (when table + (when (semanticdb-needs-refresh-p table) + (semanticdb-refresh-table table)) + (setq filemap (append filemap (oref table lexical-table))) + ) + filemap + )) + +(defun ede-emacs-find-in-directories (name base dirs) + "Find NAME is BASE directory sublist of DIRS." + (let ((ans nil)) + (while (and dirs (not ans)) + (let* ((D (car dirs)) + (ed (expand-file-name D base)) + (ef (expand-file-name name ed))) + (if (file-exists-p ef) + (setq ans ef) + ;; Not in this dir? How about subdirs? + (let ((dirfile (directory-files ed t)) + (moredirs nil) + ) + ;; Get all the subdirs. + (dolist (DF dirfile) + (when (and (file-directory-p DF) + (not (string-match "\\.$" DF))) + (push DF moredirs))) + ;; Try again. + (setq ans (ede-emacs-find-in-directories name ed moredirs)) + )) + (setq dirs (cdr dirs)))) + ans)) + +(defmethod ede-expand-filename-impl ((proj ede-emacs-project) name) + "Within this project PROJ, find the file NAME. +Knows about how the Emacs source tree is organized." + (let* ((ext (file-name-extension name)) + (root (ede-project-root proj)) + (dir (ede-project-root-directory root)) + (dirs (cond + ((not ext) nil) + ((string-match "h\\|c" ext) + '("src" "lib-src" "lwlib")) + ((string-match "elc?" ext) + '("lisp")) + ((string-match "texi" ext) + '("doc")) + (t nil))) + ) + (if (not dirs) (call-next-method) + (ede-emacs-find-in-directories name dir dirs)) + )) + +(provide 'ede/emacs) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: ede/loaddefs +;; generated-autoload-load-name: "ede/emacs" +;; End: + +;;; ede/emacs.el ends here diff --git a/lisp/cedet/ede/files.el b/lisp/cedet/ede/files.el new file mode 100644 index 00000000000..148ebb382da --- /dev/null +++ b/lisp/cedet/ede/files.el @@ -0,0 +1,516 @@ +;;; ede/files.el --- Associate projects with files and directories. + +;; Copyright (C) 2008, 2009 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: +;; +;; Directory and File scanning and matching functions. +;; +;; Basic Model: +;; +;; A directory belongs to a project if a ede-project-autoload structure +;; matches your directory. +;; +;; A toplevel project is one where there is no active project above +;; it. Finding the toplevel project involves going up a directory +;; till no ede-project-autoload structure matches. +;; + +(require 'ede) + +(declare-function ede-locate-file-in-hash "ede/locate") +(declare-function ede-locate-add-file-to-hash "ede/locate") +(declare-function ede-locate-file-in-project "ede/locate") + +(defvar ede--disable-inode nil + "Set to 't' to simulate systems w/out inode support.") + +;;; Code: +;;;###autoload +(defun ede-find-file (file) + "Find FILE in project. FILE can be specified without a directory. +There is no completion at the prompt. FILE is searched for within +the current EDE project." + (interactive "sFile: ") + (let ((fname (ede-expand-filename (ede-current-project) file)) + ) + (unless fname + (error "Could not find %s in %s" + file + (ede-project-root-directory (ede-current-project)))) + (find-file fname))) + +;;; Placeholders for ROOT directory scanning on base objects +;; +(defmethod ede-project-root ((this ede-project-placeholder)) + "If a project knows it's root, return it here. +Allows for one-project-object-for-a-tree type systems." + (oref this rootproject)) + +(defmethod ede-project-root-directory ((this ede-project-placeholder) + &optional file) + "If a project knows it's root, return it here. +Allows for one-project-object-for-a-tree type systems. +Optional FILE is the file to test. It is ignored in preference +of the anchor file for the project." + (file-name-directory (expand-file-name (oref this file)))) + + +(defmethod ede-project-root ((this ede-project-autoload)) + "If a project knows it's root, return it here. +Allows for one-project-object-for-a-tree type systems." + nil) + +(defmethod ede-project-root-directory ((this ede-project-autoload) + &optional file) + "If a project knows it's root, return it here. +Allows for one-project-object-for-a-tree type systems. +Optional FILE is the file to test. If there is no FILE, use +the current buffer." + (when (not file) + (setq file default-directory)) + (when (slot-boundp this :proj-root) + (let ((rootfcn (oref this proj-root))) + (when rootfcn + (condition-case nil + (funcall rootfcn file) + (error + (funcall rootfcn))) + )))) + +(defmethod ede--project-inode ((proj ede-project-placeholder)) + "Get the inode of the directory project PROJ is in." + (if (slot-boundp proj 'dirinode) + (oref proj dirinode) + (oset proj dirinode (ede--inode-for-dir (oref proj :directory))))) + +(defmethod ede-find-subproject-for-directory ((proj ede-project-placeholder) + dir) + "Find a subproject of PROJ that corresponds to DIR." + (if ede--disable-inode + (let ((ans nil)) + ;; Try to find the right project w/out inodes. + (ede-map-subprojects + proj + (lambda (SP) + (when (not ans) + (if (string= (file-truename dir) (oref SP :directory)) + (setq ans SP) + (ede-find-subproject-for-directory SP dir))))) + ans) + ;; We can use inodes, so lets try it. + (let ((ans nil) + (inode (ede--inode-for-dir dir))) + (ede-map-subprojects + proj + (lambda (SP) + (when (not ans) + (if (equal (ede--project-inode SP) inode) + (setq ans SP) + (ede-find-subproject-for-directory SP dir))))) + ans))) + +;;; DIRECTORY IN OPEN PROJECT +;; +;; These routines match some directory name to one of the many pre-existing +;; open projects. This should avoid hitting the disk, or asking lots of questions +;; if used throughout the other routines. +(defvar ede-inode-directory-hash (make-hash-table + ;; Note on test. Can we compare inodes or something? + :test 'equal) + "A hash of directory names and inodes.") + +(defun ede--put-inode-dir-hash (dir inode) + "Add to the EDE project hash DIR associated with INODE." + (when (fboundp 'puthash) + (puthash dir inode ede-inode-directory-hash) + inode)) + +(defun ede--get-inode-dir-hash (dir) + "Get the EDE project hash DIR associated with INODE." + (when (fboundp 'gethash) + (gethash dir ede-inode-directory-hash) + )) + +(defun ede--inode-for-dir (dir) + "Return the inode for the directory DIR." + (let ((hashnode (ede--get-inode-dir-hash (expand-file-name dir)))) + (or hashnode + (if ede--disable-inode + (ede--put-inode-dir-hash dir 0) + (let ((fattr (file-attributes dir))) + (ede--put-inode-dir-hash dir (nth 10 fattr)) + ))))) + +(defun ede-directory-get-open-project (dir &optional rootreturn) + "Return an already open project that is managing DIR. +Optional ROOTRETURN specifies a symbol to set to the root project. +If DIR is the root project, then it is the same." + (let* ((inode (ede--inode-for-dir dir)) + (ft (file-name-as-directory (expand-file-name dir))) + (proj (ede--inode-get-toplevel-open-project inode)) + (ans nil)) + ;; Try file based search. + (when (not proj) + (setq proj (ede-directory-get-toplevel-open-project ft))) + ;; Default answer is this project + (setq ans proj) + ;; Save. + (when rootreturn (set rootreturn proj)) + ;; Find subprojects. + (when (and proj (or ede--disable-inode + (not (equal inode (ede--project-inode proj))))) + (setq ans (ede-find-subproject-for-directory proj ft))) + ans)) + +(defun ede--inode-get-toplevel-open-project (inode) + "Return an already open toplevel project that is managing INODE. +Does not check subprojects." + (when (or (and (numberp inode) (/= inode 0)) + (consp inode)) + (let ((all ede-projects) + (found nil) + ) + (while (and all (not found)) + (when (equal inode (ede--project-inode (car all))) + (setq found (car all))) + (setq all (cdr all))) + found))) + +(defun ede-directory-get-toplevel-open-project (dir) + "Return an already open toplevel project that is managing DIR." + (let ((ft (file-name-as-directory (expand-file-name dir))) + (all ede-projects) + (ans nil)) + (while (and all (not ans)) + ;; Do the check. + (let ((pd (oref (car all) :directory)) + ) + (cond + ;; Exact text match. + ((string= pd ft) + (setq ans (car all))) + ;; Some sub-directory + ((string-match (concat "^" (regexp-quote pd)) ft) + (setq ans (car all))) + ;; Exact inode match. Useful with symlinks or complex automounters. + ((let ((pin (ede--project-inode (car all))) + (inode (ede--inode-for-dir dir))) + (and (not (eql pin 0)) (equal pin inode))) + (setq ans (car all))) + ;; Subdir via truename - slower by far, but faster than a traditional lookup. + ((let ((ftn (file-truename ft)) + (ptd (file-truename (oref (car all) :directory)))) + (string-match (concat "^" (regexp-quote ptd)) ftn)) + (setq ans (car all))) + )) + (setq all (cdr all))) + ans)) + +;;; DIRECTORY-PROJECT-P +;; +;; For a fresh buffer, or for a path w/ no open buffer, use this +;; routine to determine if there is a known project type here. +(defvar ede-project-directory-hash (make-hash-table + ;; Note on test. Can we compare inodes or something? + :test 'equal) + "A hash of directory names and associated EDE objects.") + +(defun ede-project-directory-remove-hash (dir) + "Reset the directory hash for DIR. +Do this whenever a new project is created, as opposed to loaded." + ;; TODO - Use maphash, and delete by regexp, not by dir searching! + + (when (fboundp 'remhash) + (remhash (file-name-as-directory dir) ede-project-directory-hash) + ;; Look for all subdirs of D, and remove them. + (let ((match (concat "^" (regexp-quote dir)))) + (maphash (lambda (K O) + (when (string-match match K) + (remhash K ede-project-directory-hash))) + ede-project-directory-hash)) + )) + +(defun ede-directory-project-from-hash (dir) + "If there is an already loaded project for DIR, return it from the hash." + (when (fboundp 'gethash) + (gethash dir ede-project-directory-hash nil))) + +(defun ede-directory-project-add-description-to-hash (dir desc) + "Add to the EDE project hash DIR associated with DESC." + (when (fboundp 'puthash) + (puthash dir desc ede-project-directory-hash) + desc)) + +(defun ede-directory-project-p (dir &optional force) + "Return a project description object if DIR has a project. +Optional argument FORCE means to ignore a hash-hit of 'nomatch. +This depends on an up to date `ede-project-class-files' variable." + (let* ((dirtest (expand-file-name dir)) + (match (ede-directory-project-from-hash dirtest))) + (cond + ((and (eq match 'nomatch) (not force)) + nil) + ((and match (not (eq match 'nomatch))) + match) + (t + (let ((types ede-project-class-files) + (ret nil)) + ;; Loop over all types, loading in the first type that we find. + (while (and types (not ret)) + (if (ede-dir-to-projectfile (car types) dirtest) + (progn + ;; We found one! Require it now since we will need it. + (require (oref (car types) file)) + (setq ret (car types)))) + (setq types (cdr types))) + (ede-directory-project-add-description-to-hash dirtest (or ret 'nomatch)) + ret))))) + +;;; TOPLEVEL +;; +;; These utilities will identify the "toplevel" of a project. +;; +(defun ede-toplevel-project-or-nil (dir) + "Starting with DIR, find the toplevel project directory, or return nil. +nil is returned if the current directory is not a part ofa project." + (let* ((ans (ede-directory-get-toplevel-open-project dir))) + (if ans + (oref ans :directory) + (if (ede-directory-project-p dir) + (ede-toplevel-project dir) + nil)))) + +(defun ede-toplevel-project (dir) + "Starting with DIR, find the toplevel project directory." + (if (and (string= dir default-directory) + ede-object-root-project) + ;; Try the local buffer cache first. + (oref ede-object-root-project :directory) + ;; Otherwise do it the hard way. + (let* ((thisdir (ede-directory-project-p dir)) + (ans (ede-directory-get-toplevel-open-project dir))) + (if (and ans ;; We have an answer + (or (not thisdir) ;; this dir isn't setup + (and (object-of-class-p ;; Same as class for this dir? + ans (oref thisdir :class-sym))) + )) + (oref ans :directory) + (let* ((toppath (expand-file-name dir)) + (newpath toppath) + (proj (ede-directory-project-p dir)) + (ans nil)) + (if proj + ;; If we already have a project, ask it what the root is. + (setq ans (ede-project-root-directory proj))) + + ;; If PROJ didn't know, or there is no PROJ, then + + ;; Loop up to the topmost project, and then load that single + ;; project, and it's sub projects. When we are done, identify the + ;; sub-project object belonging to file. + (while (and (not ans) newpath proj) + (setq toppath newpath + newpath (ede-up-directory toppath)) + (when newpath + (setq proj (ede-directory-project-p newpath))) + + (when proj + ;; We can home someone in the middle knows too. + (setq ans (ede-project-root-directory proj))) + ) + (or ans toppath)))))) + +;;; TOPLEVEL PROJECT +;; +;; The toplevel project is a way to identify the EDE structure that belongs +;; to the top of a project. + +(defun ede-toplevel (&optional subproj) + "Return the ede project which is the root of the current project. +Optional argument SUBPROJ indicates a subproject to start from +instead of the current project." + (or ede-object-root-project + (let* ((cp (or subproj (ede-current-project))) + ) + (or (and cp (ede-project-root cp)) + (progn + (while (ede-parent-project cp) + (setq cp (ede-parent-project cp))) + cp))))) + +;;; DIRECTORY CONVERSION STUFF +;; +(defmethod ede-convert-path ((this ede-project) path) + "Convert path in a standard way for a given project. +Default to making it project relative. +Argument THIS is the project to convert PATH to." + (let ((pp (ede-project-root-directory this)) + (fp (expand-file-name path))) + (if (string-match (regexp-quote pp) fp) + (substring fp (match-end 0)) + (let ((pptf (file-truename pp)) + (fptf (file-truename fp))) + (if (string-match (regexp-quote pptf) fptf) + (substring fptf (match-end 0)) + (error "Cannot convert relativize path %s" fp)))))) + +(defmethod ede-convert-path ((this ede-target) path) + "Convert path in a standard way for a given project. +Default to making it project relative. +Argument THIS is the project to convert PATH to." + (let ((proj (ede-target-parent this))) + (if proj + (let ((p (ede-convert-path proj path)) + (lp (or (oref this path) ""))) + ;; Our target THIS may have path information. + ;; strip this out of the conversion. + (if (string-match (concat "^" (regexp-quote lp)) p) + (substring p (length lp)) + p)) + (error "Parentless target %s" this)))) + +;;; FILENAME EXPANSION +;; +(defun ede-get-locator-object (proj) + "Get the locator object for project PROJ. +Get it from the toplevel project. If it doesn't have one, make one." + ;; Make sure we have a location object available for + ;; caching values, and for locating things more robustly. + (let ((top (ede-toplevel proj))) + (when (not (slot-boundp top 'locate-obj)) + (ede-enable-locate-on-project this)) + (oref top locate-obj) + )) + +(defmethod ede-expand-filename ((this ede-project) filename &optional force) + "Return a fully qualified file name based on project THIS. +FILENAME should be just a filename which occurs in a directory controlled +by this project. +Optional argument FORCE forces the default filename to be provided even if it +doesn't exist. +If FORCE equals 'newfile, then the cache is ignored." + (require 'ede/locate) + (let* ((loc (ede-get-locator-object this)) + (ha (ede-locate-file-in-hash loc filename)) + (ans nil) + ) + ;; NOTE: This function uses a locator object, which keeps a hash + ;; table of files it has found in the past. The hash table is + ;; used to make commonly found file very fast to location. Some + ;; complex routines, such as smart completion asks this question + ;; many times, so doing this speeds things up, especially on NFS + ;; or other remote file systems. + + ;; As such, special care is needed to use the hash, and also obey + ;; the FORCE option, which is needed when trying to identify some + ;; new file that needs to be created, such as a Makefile. + (cond + ;; We have a hash-table match, AND that match wasn't the 'nomatch + ;; flag, we can return it. + ((and ha (not (eq ha 'nomatch))) + (setq ans ha)) + ;; If we had a match, and it WAS no match, then we need to look + ;; at the force-option to see what to do. Since ans is already + ;; nil, then we do nothing. + ((and (eq ha 'nomatch) (not (eq force 'newfile))) + nil) + ;; We had no hash table match, so we have to look up this file + ;; using the usual EDE file expansion rules. + (t + (let ((calc (ede-expand-filename-impl this filename))) + (if calc + (progn + (ede-locate-add-file-to-hash loc filename calc) + (setq ans calc)) + ;; If we failed to calculate something, we + ;; should add it to the hash, but ONLY if we are not + ;; going to FORCE the file into existance. + (when (not force) + (ede-locate-add-file-to-hash loc filename 'nomatch)))) + )) + ;; Now that all options have been queried, if the FORCE option is + ;; true, but ANS is still nil, then we can make up a file name. + + ;; Is it forced? + (when (and force (not ans)) + (let ((dir (ede-project-root-directory this))) + (setq ans (expand-file-name filename dir)))) + + ans)) + +(defmethod ede-expand-filename-impl ((this ede-project) filename &optional force) + "Return a fully qualified file name based on project THIS. +FILENAME should be just a filename which occurs in a directory controlled +by this project. +Optional argument FORCE forces the default filename to be provided even if it +doesn't exist." + (let ((loc (ede-get-locator-object this)) + (path (ede-project-root-directory this)) + (proj (oref this subproj)) + (found nil)) + ;; find it Locally. + (setq found + (cond ((file-exists-p (expand-file-name filename path)) + (expand-file-name filename path)) + ((file-exists-p (expand-file-name (concat "include/" filename) path)) + (expand-file-name (concat "include/" filename) path)) + (t + (while (and (not found) proj) + (setq found (when (car proj) + (ede-expand-filename (car proj) filename)) + proj (cdr proj))) + found))) + ;; Use an external locate tool. + (when (not found) + (require 'ede/locate) + (setq found (car (ede-locate-file-in-project loc filename)))) + ;; Return it + found)) + +(defmethod ede-expand-filename ((this ede-target) filename &optional force) + "Return a fully qualified file name based on target THIS. +FILENAME should a a filename which occurs in a directory in which THIS works. +Optional argument FORCE forces the default filename to be provided even if it +doesn't exist." + (ede-expand-filename (ede-target-parent this) filename force)) + +;;; UTILITIES +;; + +(defun ede-up-directory (dir) + "Return a dir that is up one directory. +Argument DIR is the directory to trim upwards." + (let* ((fad (directory-file-name dir)) + (fnd (file-name-directory fad))) + (if (string= dir fnd) ; This will catch the old string-match against + ; c:/ for DOS like systems. + nil + fnd))) + +(provide 'ede/files) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: ede/loaddefs +;; generated-autoload-load-name: "ede/files" +;; End: + +;;; ede/files.el ends here diff --git a/lisp/cedet/ede/linux.el b/lisp/cedet/ede/linux.el new file mode 100644 index 00000000000..e95dce6d1f5 --- /dev/null +++ b/lisp/cedet/ede/linux.el @@ -0,0 +1,237 @@ +;;; ede/linux.el --- Special project for Linux + +;; Copyright (C) 2008, 2009 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: +;; +;; Provide a special project type just for Linux, cause Linux is special. +;; +;; Identifies a Linux project automatically. +;; Speedy ede-expand-filename based on extension. +;; Pre-populates the preprocessor map from lisp.h +;; +;; ToDo : +;; * Add "build" options. +;; * Add texinfo lookup options. +;; * Add website + +(require 'ede) +(declare-function semanticdb-file-table-object "semantic/db") +(declare-function semanticdb-needs-refresh-p "semantic/db") +(declare-function semanticdb-refresh-table "semantic/db") + +;;; Code: +(defvar ede-linux-project-list nil + "List of projects created by option `ede-linux-project'.") + +(defun ede-linux-file-existing (dir) + "Find a Linux project in the list of Linux projects. +DIR is the directory to search from." + (let ((projs ede-linux-project-list) + (ans nil)) + (while (and projs (not ans)) + (let ((root (ede-project-root-directory (car projs)))) + (when (string-match (concat "^" (regexp-quote root)) dir) + (setq ans (car projs)))) + (setq projs (cdr projs))) + ans)) + +;;;###autoload +(defun ede-linux-project-root (&optional dir) + "Get the root directory for DIR." + (when (not dir) (setq dir default-directory)) + (let ((case-fold-search t) + (proj (ede-linux-file-existing dir))) + (if proj + (ede-up-directory (file-name-directory + (oref proj :file))) + ;; No pre-existing project. Lets take a wild-guess if we have + ;; an Linux project here. + (when (string-match "linux[^/]*" dir) + (let ((base (substring dir 0 (match-end 0)))) + (when (file-exists-p (expand-file-name "scripts/ver_linux" base)) + base)))))) + +(defun ede-linux-version (dir) + "Find the Linux version for the Linux src in DIR." + (let ((buff (get-buffer-create " *linux-query*"))) + (save-excursion + (set-buffer buff) + (erase-buffer) + (setq default-directory (file-name-as-directory dir)) + (call-process "head" nil buff nil "-n" "3" "Makefile") + (goto-char (point-min)) + (let (major minor sub) + (re-search-forward "^VERSION *= *\\([0-9.]+\\)") + (setq major (match-string 1)) + (re-search-forward "^PATCHLEVEL *= *\\([0-9.]+\\)") + (setq minor (match-string 1)) + (re-search-forward "^SUBLEVEL *= *\\([0-9.]+\\)") + (setq sub (match-string 1)) + (prog1 + (concat major "." minor "." sub) + (kill-buffer buff) + ))))) + +(defclass ede-linux-project (ede-project eieio-instance-tracker) + ((tracking-symbol :initform 'ede-linux-project-list) + ) + "Project Type for the Linux source code." + :method-invocation-order :depth-first) + +(defun ede-linux-load (dir &optional rootproj) + "Return an Linux 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." + (or (ede-linux-file-existing dir) + ;; Doesn't already exist, so lets make one. + (ede-linux-project "Linux" + :name (concat "Linux" (ede-linux-version dir)) + :directory dir + :file (expand-file-name "scripts/ver_linux" + dir)) + (ede-add-project-to-global-list this) + ) + ) + +(defclass ede-linux-target-c (ede-target) + () + "EDE Linux Project target for C code. +All directories need at least one target.") + +(defclass ede-linux-target-misc (ede-target) + () + "EDE Linux Project target for Misc files. +All directories need at least one target.") + +(defmethod initialize-instance ((this ede-linux-project) + &rest fields) + "Make sure the :file is fully expanded." + (call-next-method) + (unless (slot-boundp this 'targets) + (oset this :targets nil))) + +;;; File Stuff +;; +(defmethod ede-project-root-directory ((this ede-linux-project) + &optional file) + "Return the root for THIS Linux project with file." + (ede-up-directory (file-name-directory (oref this file)))) + +(defmethod ede-project-root ((this ede-linux-project)) + "Return my root." + this) + +(defmethod ede-find-subproject-for-directory ((proj ede-linux-project) + dir) + "Return PROJ, for handling all subdirs below DIR." + proj) + +;;; TARGET MANAGEMENT +;; +(defun ede-linux-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-linux-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))) + (cls (cond ((not ext) + 'ede-linux-target-misc) + ((string-match "c\\|h" ext) + 'ede-linux-target-c) + (t 'ede-linux-target-misc))) + (targets (oref proj targets)) + (dir default-directory) + (ans (ede-linux-find-matching-target cls dir targets)) + ) + (when (not ans) + (setq ans (make-instance + cls + :name (file-name-nondirectory + (directory-file-name dir)) + :path dir + :source nil)) + (object-add-to-list proj :targets ans) + ) + ans)) + +;;; UTILITIES SUPPORT. +;; +(defmethod ede-preprocessor-map ((this ede-linux-target-c)) + "Get the pre-processor map for Linux C code. +All files need the macros from lisp.h!" + (require 'semantic/db) + (let* ((proj (ede-target-parent this)) + (root (ede-project-root proj)) + (versionfile (ede-expand-filename root "include/linux/version.h")) + (table (when (and versionfile (file-exists-p versionfile)) + (semanticdb-file-table-object versionfile))) + (filemap '( ("__KERNEL__" . "") + )) + ) + (when table + (when (semanticdb-needs-refresh-p table) + (semanticdb-refresh-table table)) + (setq filemap (append filemap (oref table lexical-table))) + ) + filemap + )) + +(defun ede-linux-file-exists-name (name root subdir) + "Return a file name if NAME exists under ROOT with SUBDIR in between." + (let ((F (expand-file-name name (expand-file-name subdir root)))) + (when (file-exists-p F) F))) + +(defmethod ede-expand-filename-impl ((proj ede-linux-project) name) + "Within this project PROJ, find the file NAME. +Knows about how the Linux source tree is organized." + (let* ((ext (file-name-extension name)) + (root (ede-project-root proj)) + (dir (ede-project-root-directory root)) + (F (cond + ((not ext) nil) + ((string-match "h" ext) + (or (ede-linux-file-exists-name name dir "") + (ede-linux-file-exists-name name dir "include")) + ) + ((string-match "txt" ext) + (ede-linux-file-exists-name name dir "Documentation")) + (t nil))) + ) + (or F (call-next-method)))) + +(provide 'ede/linux) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: ede/loaddefs +;; generated-autoload-load-name: "ede/linux" +;; End: + +;;; ede/linux.el ends here diff --git a/lisp/cedet/ede/locate.el b/lisp/cedet/ede/locate.el new file mode 100644 index 00000000000..100618a86c6 --- /dev/null +++ b/lisp/cedet/ede/locate.el @@ -0,0 +1,328 @@ +;;; ede/locate.el --- Locate support + +;; Copyright (C) 2008, 2009 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: +;; +;; Support for various LOCATE type functions. +;; +;; A key feature of EDE is `ede-expand-filename', which allows a +;; project to expand a filename reference in one file to some actual +;; filename. +;; +;; In that way, you may #include <foo.h>, and without knowing how to +;; read a Makefile, find it in <root>/include/foo.h. +;; +;; Some projects are regular, such as the Emacs project. Some +;; projects are completely controlled by EDE, such sh the Project.ede +;; based projects. +;; +;; For other projects, haveing a "quick hack" to support these location +;; routines is handy. +;; +;; The baseclass `ede-locate-base' provides the abstract interface to +;; finding files in a project. +;; +;; New location routines will subclass `ede-locate-base'. +;; +;; How to use: +;; +;; Configure `ede-locate-setup-options' to add the types of locate +;; features you have available. EDE will then enable the correct one +;; when it is available. + +(require 'ede) +(eval-when-compile (require 'data-debug) + (require 'eieio-datadebug) + (require 'cedet-global) + (require 'cedet-idutils) + (require 'cedet-cscope)) + +(require 'locate) + +;;; Code: +(defcustom ede-locate-setup-options + '(ede-locate-base) + "List of locate objects to try out by default. +Listed in order of preference. If the first item cannot be used in +a particular project, then the next one is tried. +It is always assumed that `ede-locate-base' is at end of the list." + :group 'ede + :type '(repeat + (choice (const :tag "None" ede-locate-base) + (const :tag "locate" ede-locate-locate) + (const :tag "GNU Global" ede-locate-global) + (const :tag "ID Utils" ede-locate-idutils) + (const :tag "CScope" ede-locate-cscope))) + ) + +;;;###autoload +(defun ede-enable-locate-on-project (&optional project) + "Enable an EDE locate feature on PROJECT. +Attempt to guess which project locate style to use +based on `ede-locate-setup-options'." + (interactive) + (let* ((proj (or project (ede-toplevel))) + (root (ede-project-root-directory proj)) + (opts ede-locate-setup-options) + (ans nil)) + (while (and opts (not ans)) + (when (ede-locate-ok-in-project (car opts) root) + ;; If interactive, check with the user. + (when (or (not (interactive-p)) + (y-or-n-p (format "Set project locator to %s? " (car opts)))) + (setq ans (car opts)))) + (setq opts (cdr opts))) + ;; No match? Always create the baseclass for the hashing tool. + (when (not ans) + (when (interactive-p) + (message "Setting locator to ede-locate-base")) + (setq ans 'ede-locate-base)) + (oset proj locate-obj (make-instance ans "Loc" :root root)) + (when (interactive-p) + (message "Satting locator to %s." ans)) + )) + +;;; LOCATE BASECLASS +;; +;; The baseclass for all location style queries. +(defclass ede-locate-base () + ((root :initarg :root + :documentation + "The root of these locat searches.") + (file :documentation + "The last file search for with EDE locate.") + (lastanswer :documentation + "The last answer provided by the locator.") + (hash :documentation + "Hash table of previously found files.") + ) + "Baseclass for LOCATE feature in EDE.") + +(defmethod initialize-instance ((loc ede-locate-base) &rest fields) + "Make sure we have a hash table." + ;; Basic setup. + (call-next-method) + ;; Make sure we have a hash table. + (oset loc hash (make-hash-table :test 'equal)) + ) + +(defmethod ede-locate-ok-in-project :static ((loc ede-locate-base) + root) + "Is it ok to use this project type under ROOT." + t) + +(defmethod ede-locate-file-in-hash ((loc ede-locate-base) + filestring) + "For LOC, is the file FILESTRING in our hashtable?" + (gethash filestring (oref loc hash))) + +(defmethod ede-locate-add-file-to-hash ((loc ede-locate-base) + filestring fullfilename) + "For LOC, add FILESTR to the hash with FULLFILENAME." + (puthash filestring fullfilename (oref loc hash))) + +(defmethod ede-locate-file-in-project ((loc ede-locate-base) + filesubstring + ) + "Locate with LOC occurances of FILESUBSTRING. +Searches are done under the current root of the EDE project +that crated this ede locat object." + (let ((ans (ede-locate-file-in-project-impl loc filesubstring)) + ) + (oset loc file filesubstring) + (oset loc lastanswer ans) + ans)) + +(defmethod ede-locate-file-in-project-impl ((loc ede-locate-base) + filesubstring + ) + "Locate with LOC occurances of FILESUBSTRING. +Searches are done under the current root of the EDE project +that crated this ede locat object." + nil + ) + +;;; LOCATE +;; +;; Using the standard unix "locate" command. +;; Since locate is system wide, we need to hack the search +;; to restrict it to within just this project. + +(defclass ede-locate-locate (ede-locate-base) + () + "EDE Locator using the locate command. +Configure the Emacs `locate-program' variable to also +configure the use of EDE locate.") + +(defmethod ede-locate-ok-in-project :static ((loc ede-locate-locate) + root) + "Is it ok to use this project type under ROOT." + (or (featurep 'locate) (locate-library "locate")) + ) + +(defmethod ede-locate-file-in-project-impl ((loc ede-locate-locate) + filesubstring) + "Locate with LOC occurances of FILESUBSTRING under PROJECTROOT. +Searches are done under the current root of the EDE project +that crated this ede locat object." + ;; We want something like: + ;; /my/project/root*/filesubstring.c + (let* ((searchstr (concat (directory-file-name (oref loc root)) + "*/" filesubstring)) + (b (get-buffer-create "*LOCATE*")) + (cd default-directory) + ) + (save-excursion + (set-buffer b) + (setq default-directory cd) + (erase-buffer)) + (apply 'call-process locate-command + nil b nil + searchstr nil) + (save-excursion + (set-buffer b) + (split-string (buffer-string) "\n" t)) + ) + ) + +;;; GLOBAL +;; +(defclass ede-locate-global (ede-locate-base) + () + "EDE Locator using GNU Global. +Configure EDE's use of GNU Global through the cedet-global.el +variable `cedet-global-command'.") + +(defmethod initialize-instance ((loc ede-locate-global) + &rest slots) + "Make sure that we can use GNU Global." + (require 'cedet-global) + ;; Get ourselves initialized. + (call-next-method) + ;; Do the checks. + (cedet-gnu-global-version-check) + (let* ((default-directory (oref loc root)) + (root (cedet-gnu-global-root))) + (when (not root) + (error "Cannot use GNU Global in %s" + (oref loc root)))) + ) + +(defmethod ede-locate-ok-in-project :static ((loc ede-locate-global) + root) + "Is it ok to use this project type under ROOT." + (require 'cedet-global) + (cedet-gnu-global-version-check) + (let* ((default-directory root) + (newroot (cedet-gnu-global-root))) + newroot)) + +(defmethod ede-locate-file-in-project-impl ((loc ede-locate-global) + filesubstring) + "Locate with LOC occurances of FILESUBSTRING under PROJECTROOT. +Searches are done under the current root of the EDE project +that crated this ede locat object." + (require 'cedet-global) + (let ((default-directory (oref loc root))) + (cedet-gnu-global-expand-filename filesubstring))) + +;;; IDUTILS +;; +(defclass ede-locate-idutils (ede-locate-base) + () + "EDE Locator using IDUtils. +Configure EDE's use of IDUtils through the cedet-idutils.el +file name searching variable `cedet-idutils-file-command'.") + +(defmethod initialize-instance ((loc ede-locate-idutils) + &rest slots) + "Make sure that we can use IDUtils." + ;; Get ourselves initialized. + (call-next-method) + ;; Do the checks. + (require 'cedet-idutils) + (cedet-idutils-version-check) + (when (not (cedet-idutils-support-for-directory (oref loc root))) + (error "Cannot use IDUtils in %s" + (oref loc root))) + ) + +(defmethod ede-locate-ok-in-project :static ((loc ede-locate-idutils) + root) + "Is it ok to use this project type under ROOT." + (require 'cedet-idutils) + (cedet-idutils-version-check) + (when (cedet-idutils-support-for-directory root) + root)) + +(defmethod ede-locate-file-in-project-impl ((loc ede-locate-idutils) + filesubstring) + "Locate with LOC occurances of FILESUBSTRING under PROJECTROOT. +Searches are done under the current root of the EDE project +that crated this ede locat object." + (require 'cedet-idutils) + (let ((default-directory (oref loc root))) + (cedet-idutils-expand-filename filesubstring))) + +;;; CSCOPE +;; +(defclass ede-locate-cscope (ede-locate-base) + () + "EDE Locator using Cscope. +Configure EDE's use of Cscope through the cedet-cscope.el +file name searching variable `cedet-cscope-file-command'.") + +(defmethod initialize-instance ((loc ede-locate-cscope) + &rest slots) + "Make sure that we can use Cscope." + ;; Get ourselves initialized. + (call-next-method) + ;; Do the checks. + (cedet-cscope-version-check) + (when (not (cedet-cscope-support-for-directory (oref loc root))) + (error "Cannot use Cscope in %s" + (oref loc root))) + ) + +(defmethod ede-locate-ok-in-project :static ((loc ede-locate-cscope) + root) + "Is it ok to use this project type under ROOT." + (cedet-cscope-version-check) + (when (cedet-cscope-support-for-directory root) + root)) + +(defmethod ede-locate-file-in-project-impl ((loc ede-locate-cscope) + filesubstring) + "Locate with LOC occurances of FILESUBSTRING under PROJECTROOT. +Searches are done under the current root of the EDE project +that crated this ede locat object." + (let ((default-directory (oref loc root))) + (cedet-cscope-expand-filename filesubstring))) + +(provide 'ede/locate) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: ede/loaddefs +;; generated-autoload-load-name: "ede/locate" +;; End: + +;;; ede/locate.el ends here diff --git a/lisp/cedet/ede/make.el b/lisp/cedet/ede/make.el new file mode 100644 index 00000000000..08598d08161 --- /dev/null +++ b/lisp/cedet/ede/make.el @@ -0,0 +1,110 @@ +;;; ede/make.el --- General information about "make" + +;;; Copyright (C) 2009 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: +;; +;; This file needs to choose the version of "make" it wants to use. +;; Whenever an executable "gmake" is available, we prefer that since +;; it usually means GNU Make. If it doesn't exist, use "make". +;; +;; Run tests on make --version to be sure it is GNU make so that +;; logical error messages can be provided. + +;;; Code: + +(declare-function inversion-check-version "inversion") + +(if (fboundp 'locate-file) + (defsubst ede--find-executable (exec) + "Return an expanded file name for a program EXEC on the exec path." + (locate-file exec exec-path)) + + ;; Else, older version of Emacs. + + (defsubst ede--find-executable (exec) + "Return an expanded file name for a program EXEC on the exec path." + (let ((p exec-path) + (found nil)) + (while (and p (not found)) + (let ((f (expand-file-name exec (car p)))) + (if (file-exists-p f) + (setq found f))) + (setq p (cdr p))) + found)) + ) + +(defvar ede-make-min-version "3.0" + "Minimum version of GNU make required.") + +(defcustom ede-make-command (cond ((ede--find-executable "gmake") + "gmake") + (t "make")) ;; What to do? + "The MAKE command to use for EDE when compiling. +The makefile generated by EDE for C files uses syntax that depends on GNU Make, +so this should be set to something that can execute GNU Make files." + :group 'ede + :type 'string) + +;;;###autoload +(defun ede-make-check-version (&optional noerror) + "Check the version of GNU Make installed. +The check passes if the MAKE version is no high enough, or if it +is not GNU make. +If NOERROR is non-nil, return t for success, nil for failure. +If NOERROR is nil, then throw an error on failure. Return t otherwise." + (interactive) + (let ((b (get-buffer-create "*EDE Make Version*")) + (cd default-directory) + (rev nil) + (ans nil) + ) + (save-excursion + ;; Setup, and execute make. + (set-buffer b) + (setq default-directory cd) + (erase-buffer) + (call-process ede-make-command nil b nil + "--version") + ;; Check the buffer for the string + (goto-char (point-min)) + (when (looking-at "GNU Make\\(?: version\\)? \\([0-9][^,]+\\),") + (setq rev (match-string 1)) + (require 'inversion) + (setq ans (not (inversion-check-version rev nil ede-make-min-version)))) + + ;; Answer reporting. + (when (and (interactive-p) ans) + (message "GNU Make version %s. Good enough for CEDET." rev)) + + (when (and (not noerror) (not ans)) + (error "EDE requires GNU Make version %s or later. Configure `ede-make-command' to fix" + ede-make-min-version)) + ans))) + +(provide 'ede/make) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: ede/loaddefs +;; generated-autoload-load-name: "ede/make" +;; End: + +;;; ede/make.el ends here diff --git a/lisp/cedet/ede/makefile-edit.el b/lisp/cedet/ede/makefile-edit.el new file mode 100644 index 00000000000..7810657a3af --- /dev/null +++ b/lisp/cedet/ede/makefile-edit.el @@ -0,0 +1,129 @@ +;;; makefile-edit.el --- Makefile editing/scanning commands. + +;;; Copyright (C) 2009 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: +;; +;; Utilities for editing a Makefile for EDE Makefile management commands. +;; +;; Derived from project-am.el. +;; +;; Makefile editing and scanning commands +;; +;; Formatting of a makefile +;; +;; 1) Creating an automakefile, stick in a top level comment about +;; being created by emacs +;; 2) Leave order of variable contents alone, except for SOURCE +;; SOURCE always keep in the order of .c, .h, the other stuff. + +;;; Things to do +;; makefile-fill-paragraph -- refill a macro w/ backslashes +;; makefile-insert-macro -- insert "foo = " + + +;;; Code: + +(defun makefile-beginning-of-command () + "Move the the beginning of the current command." + (interactive) + (if (save-excursion + (forward-line -1) + (makefile-line-continued-p)) + (forward-line -1)) + (beginning-of-line) + (if (not (makefile-line-continued-p)) + nil + (while (and (makefile-line-continued-p) + (not (bobp))) + (forward-line -1)) + (forward-line 1))) + +(defun makefile-end-of-command () + "Move the the beginning of the current command." + (interactive) + (end-of-line) + (while (and (makefile-line-continued-p) + (not (eobp))) + (forward-line 1) + (end-of-line))) + +(defun makefile-line-continued-p () + "Return non-nil if the current line ends in continuation." + (save-excursion + (end-of-line) + (= (preceding-char) ?\\))) + +;;; Programatic editing of a Makefile +;; +(defun makefile-move-to-macro (macro &optional next) + "Move to the definition of MACRO. Return t if found. +If NEXT is non-nil, move to the next occurance of MACRO." + (let ((oldpt (point))) + (when (not next) (goto-char (point-min))) + (if (re-search-forward (concat "^\\s-*" macro "\\s-*[+:?]?=") nil t) + t + (goto-char oldpt) + nil))) + +(defun makefile-navigate-macro (stop-before) + "In a list of files, move forward until STOP-BEFORE is reached. +STOP-BEFORE is a regular expression matching a file name." + (save-excursion + (makefile-beginning-of-command) + (let ((e (save-excursion + (makefile-end-of-command) + (point)))) + (if (re-search-forward stop-before nil t) + (goto-char (match-beginning 0)) + (goto-char e))))) + +(defun makefile-macro-file-list (macro) + "Return a list of all files in MACRO." + (save-excursion + (goto-char (point-min)) + (let ((lst nil)) + (while (makefile-move-to-macro macro t) + (let ((e (save-excursion + (makefile-end-of-command) + (point)))) + (while (re-search-forward "\\s-**\\([-a-zA-Z0-9./_@$%(){}]+\\)\\s-*" e t) + (let ((var nil)(varexp nil) + (match (buffer-substring-no-properties + (match-beginning 1) + (match-end 1)))) + (if (not (setq var (makefile-extract-varname-from-text match))) + (setq lst (cons match lst)) + (setq varexp (makefile-macro-file-list var)) + (dolist (V varexp) + (setq lst (cons V lst)))))))) + (nreverse lst)))) + +(defun makefile-extract-varname-from-text (text) + "Extract the variable name from TEXT if it is a variable reference. +Return nil if it isn't a variable." + (save-match-data + (when (string-match "\\$\\s(\\([A-Za-z0-9_]+\\)\\s)" text) + (match-string 1 text)))) + + +(provide 'ede/makefile-edit) + +;;; ede/makefile-edit.el ends here diff --git a/lisp/cedet/ede/pconf.el b/lisp/cedet/ede/pconf.el new file mode 100644 index 00000000000..62adf076f10 --- /dev/null +++ b/lisp/cedet/ede/pconf.el @@ -0,0 +1,188 @@ +;;; ede/pconf.el --- configure.ac maintenance for EDE + +;;; Copyright (C) 1998, 1999, 2000, 2005, 2008, 2009 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Keywords: project + +;; 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: +;; +;; Code generator for autoconf configure.ac, and support files. + +(require 'ede/proj) +(require 'ede/autoconf-edit) +(defvar compilation-in-progress) + +(defvar ede-pconf-create-file-query 'ask + "Controls if queries are made while creating project files. +A value of 'ask means to always ask the user before creating +a file, such as AUTHORS. A value of 'never means don't ask, and +don't do it. A value of nil means to just do it.") + +;;; Code: +(defmethod ede-proj-configure-file ((this ede-proj-project)) + "The configure.ac script used by project THIS." + (ede-expand-filename (ede-toplevel this) "configure.ac" t)) + +(defmethod ede-proj-configure-test-required-file ((this ede-proj-project) file) + "For project THIS, test that the file FILE exists, or create it." + (when (not (ede-expand-filename (ede-toplevel this) file)) + (save-excursion + (find-file (ede-expand-filename (ede-toplevel this) file t)) + (cond ((string= file "AUTHORS") + (insert (user-full-name) " <" (user-login-name) ">")) + ((string= file "NEWS") + (insert "NEWS file for " (ede-name this))) + (t (insert "\n"))) + (save-buffer) + (when + (and (eq ede-pconf-create-file-query 'ask) + (not (eq ede-pconf-create-file-query 'never)) + (not (y-or-n-p + (format "I had to create the %s file for you. Ok? " file))) + (error "Quit")))))) + + +(defmethod ede-proj-configure-synchronize ((this ede-proj-project)) + "Synchronize what we know about project THIS into configure.ac." + (let ((b (find-file-noselect (ede-proj-configure-file this))) + ;;(td (file-name-directory (ede-proj-configure-file this))) + (targs (oref this targets)) + (postcmd "") + (add-missing nil)) + ;; First, make sure we have a file. + (if (not (file-exists-p (ede-proj-configure-file this))) + (autoconf-new-program b (oref this name) "Project.ede")) + (set-buffer b) + ;; Next, verify all targets of all subobjects. + (autoconf-set-version (oref this version)) + (let ((top-level-project-local this)) + (autoconf-set-output + (ede-map-all-subprojects + this + (lambda (sp) + ;; NOTE: don't put in ./Makefile - configure complains. + (let ((dir (file-name-as-directory + (directory-file-name + (ede-subproject-relative-path sp top-level-project-local))))) + (when (string= dir "./") (setq dir "")) + ;; Use concat, because expand-file-name removes the relativeness. + (concat dir "Makefile") ))))) + ;; + ;; NOTE TO SELF. TURN THIS INTO THE OFFICIAL LIST + ;; + (ede-proj-dist-makefile this) + ;; Loop over all targets to clean and then add themselves in. + (ede-map-all-subprojects + this + (lambda (sp) + (ede-map-targets sp 'ede-proj-flush-autoconf))) + (ede-map-all-subprojects + this + (lambda (sp) + (ede-map-targets this 'ede-proj-tweak-autoconf))) + ;; Now save + (save-buffer) + ;; Verify aclocal + (setq postcmd "aclocal;") + ;; Always add missing files as needed. + (setq postcmd (concat postcmd "automake --add-missing;")) + + ;; Always do autoreconf + (setq postcmd (concat postcmd "autoreconf;")) + ;; Verify a bunch of files that are required by automake. + (ede-proj-configure-test-required-file this "AUTHORS") + (ede-proj-configure-test-required-file this "NEWS") + (ede-proj-configure-test-required-file this "README") + (ede-proj-configure-test-required-file this "ChangeLog") + ;; Let specific targets get missing files. + (mapc 'ede-proj-configure-create-missing targs) + ;; Verify that we have a make system. + (if (or (not (ede-expand-filename (ede-toplevel this) "Makefile")) + ;; Now is this one of our old Makefiles? + (save-excursion + (set-buffer (find-file-noselect + (ede-expand-filename (ede-toplevel this) + "Makefile" t) t)) + (goto-char (point-min)) + ;; Here is the unique piece for our makefiles. + (re-search-forward "For use with: make" nil t))) + (setq postcmd (concat postcmd "./configure;"))) + (if (not (string= "" postcmd)) + (progn + (compile postcmd) + + (while compilation-in-progress + (accept-process-output) + (sit-for 1)) + + (save-excursion + (set-buffer "*compilation*") + (goto-char (point-max)) + + (when (not (string= mode-line-process ":exit [0]")) + (error "Configure failed!")) + + ;; The Makefile is now recreated by configure? + (let ((b (get-file-buffer + (ede-expand-filename (ede-toplevel this) + "Makefile" 'newfile)))) + ;; This makes sure that if Makefile was loaded, and old, + ;; that it gets flushed so we don't keep rebuilding + ;; the autoconf system. + (if b (kill-buffer b)))) + + )))) + +(defmethod ede-proj-configure-recreate ((this ede-proj-project)) + "Delete project THISes configure script and start over." + (if (not (ede-proj-configure-file this)) + (error "Could not determine configure.ac for %S" (object-name this))) + (let ((b (get-file-buffer (ede-proj-configure-file this)))) + ;; Destroy all evidence of the old configure.ac + (delete-file (ede-proj-configure-file this)) + (if b (kill-buffer b))) + (ede-proj-configure-synchronize this)) + +(defmethod ede-proj-tweak-autoconf ((this ede-proj-target)) + "Tweak the configure file (current buffer) to accomodate THIS." + ;; Check the compilers belonging to THIS, and call the autoconf + ;; setup for those compilers. + (mapc 'ede-proj-tweak-autoconf (ede-proj-compilers this)) + (mapc 'ede-proj-tweak-autoconf (ede-proj-linkers this)) + ) + +(defmethod ede-proj-flush-autoconf ((this ede-proj-target)) + "Flush the configure file (current buffer) to accomodate THIS. +By flushing, remove any cruft that may be in the file. Subsequent +calls to `ede-proj-tweak-autoconf' can restore items removed by flush." + nil) + +(defmethod ede-proj-configure-add-missing ((this ede-proj-target)) + "Query if any files needed by THIS provided by automake are missing. +Results in --add-missing being passed to automake." + nil) + +(defmethod ede-proj-configure-create-missing ((this ede-proj-target)) + "Add any missing files for THIS by creating them." + nil) + +(provide 'ede/pconf) + +;;; ede/pconf.el ends here diff --git a/lisp/cedet/ede/pmake.el b/lisp/cedet/ede/pmake.el new file mode 100644 index 00000000000..bc5fe577043 --- /dev/null +++ b/lisp/cedet/ede/pmake.el @@ -0,0 +1,659 @@ +;;; ede-pmake.el --- EDE Generic Project Makefile code generator. + +;;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, +;;; 2007, 2008, 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: +;; +;; Code generator for Makefiles. +;; +;; Here is how it should work: +;; 1) Collect information about the project and targets +;; 2) Insert header into the Makefile +;; 3) Insert basic variables (target/source) +;; 4) Conditional +;; a) Makefile +;; 1) Insert support variables (compiler variables, etc) +;; 2) Insert VERSION and DISTDIR +;; 3) Specify top build dir if necessary +;; 4) Specify compile/link commands (c, etc) +;; 5) Specify dependency files +;; 6) Specify all: target +;; 7) Include dependency files +;; 8) Insert commonized target specify rules +;; 9) Insert clean: and dist: rules +;; b) Automake file +;; 1) Insert distribution source variables for targets +;; 2) Insert user requested rules + +(require 'ede/proj) +(require 'ede/proj-obj) +(require 'ede/proj-comp) + +;;; Code: +(defmethod ede-proj-makefile-create ((this ede-proj-project) mfilename) + "Create a Makefile for all Makefile targets in THIS. +MFILENAME is the makefile to generate." + (let ((mt nil) + (isdist (string= mfilename (ede-proj-dist-makefile this))) + (depth 0) + (orig-buffer nil) + (buff-to-kill nil) + ) + ;; Find out how deep this project is. + (let ((tmp this)) + (while (setq tmp (ede-parent-project tmp)) + (setq depth (1+ depth)))) + ;; Collect the targets that belong in a makefile. + (mapc + (lambda (obj) + (if (and (obj-of-class-p obj 'ede-proj-target-makefile) + (string= (oref obj makefile) mfilename)) + (setq mt (cons obj mt)))) + (oref this targets)) + ;; Fix the order so things compile in the right direction. + (setq mt (nreverse mt)) + ;; Add in the header part of the Makefile* + (save-excursion + (setq orig-buffer (get-file-buffer mfilename)) + (set-buffer (setq buff-to-kill (find-file-noselect mfilename))) + (goto-char (point-min)) + (if (and + (not (eobp)) + (not (looking-at "# Automatically Generated \\w+ by EDE."))) + (if (not (y-or-n-p (format "Really replace %s? " mfilename))) + (error "Not replacing Makefile")) + (message "Replace EDE Makefile")) + (erase-buffer) + (ede-srecode-setup) + ;; Insert a giant pile of stuff that is common between + ;; one of our Makefiles, and a Makefile.in + (ede-srecode-insert + "file:ede-empty" + "MAKETYPE" + (with-slots (makefile-type) this + (cond ((eq makefile-type 'Makefile) "make") + ((eq makefile-type 'Makefile.in) "autoconf") + ((eq makefile-type 'Makefile.am) "automake") + (t (error ":makefile-type in project invalid"))))) + + ;; Just this project's variables + (ede-proj-makefile-insert-variables this) + + ;; Space + (insert "\n") + + (cond + ((eq (oref this makefile-type) 'Makefile) + ;; Make sure the user has the right kind of make + (ede-make-check-version) + + (let* ((targ (if isdist (oref this targets) mt)) + (sp (oref this subproj)) + (df (apply 'append + (mapcar (lambda (tg) + (ede-proj-makefile-dependency-files tg)) + targ)))) + ;; Distribution variables + (ede-compiler-begin-unique + (mapc 'ede-proj-makefile-insert-variables targ)) + ;; Only add the distribution stuff in when depth != 0 + (let ((top (ede-toplevel this)) + (tmp this) + (subdir "")) + (insert "VERSION=" (oref top version) "\n" + "DISTDIR=$(top)" (oref top name) "-$(VERSION)") + (while (ede-parent-project tmp) + (setq subdir + (concat + "/" + (file-name-nondirectory + (directory-file-name + (file-name-directory (oref tmp file)))) + subdir) + tmp (ede-parent-project tmp))) + (insert subdir "\n")) + ;; Some built in variables for C code + (if df + (let ((tc depth)) + (insert "top_builddir = ") + (while (/= 0 tc) + (setq tc (1- tc)) + (insert "..") + (if (/= tc 0) (insert "/"))) + (insert "\n"))) + (insert "\n") + ;; Create a variable with all the dependency files to include + ;; These methods borrowed from automake. + (if (and (oref this automatic-dependencies) df) + (progn + (insert "DEP_FILES=" + (mapconcat (lambda (f) + (concat ".deps/" + (file-name-nondirectory + (file-name-sans-extension + f)) ".P")) + df " ")))) + ;; + ;; Insert ALL Rule + ;; + (insert "\n\nall:") + (mapc (lambda (c) + (if (and (slot-exists-p c 'partofall) (oref c partofall)) + ;; Only insert this rule if it is a part of ALL. + (insert " " (ede-proj-makefile-target-name c)))) + targ) + (mapc (lambda (c) + (insert " " (ede-name c)) + ) + sp) + (insert "\n\n") + ;; + ;; Add in the include files + ;; + (mapc (lambda (c) + (insert "include " c "\n\n")) + (oref this include-file)) + ;; Some C inference rules + ;; Dependency rules borrowed from automake. + ;; + ;; NOTE: This is GNU Make specific. + (if (and (oref this automatic-dependencies) df) + (insert "DEPS_MAGIC := $(shell mkdir .deps > /dev/null " + "2>&1 || :)\n" + "-include $(DEP_FILES)\n\n")) + ;; + ;; General makefile rules stored in the individual targets + ;; + (ede-compiler-begin-unique + (ede-proj-makefile-insert-rules this) + (mapc 'ede-proj-makefile-insert-rules targ)) + ;; + ;; phony targets for sub projects + ;; + (mapc 'ede-proj-makefile-insert-subproj-rules sp) + ;; + ;; Distribution rules such as CLEAN and DIST + ;; + (when isdist + (ede-proj-makefile-tags this mt) + (ede-proj-makefile-insert-dist-rules this))) + (save-buffer)) + ((eq (oref this makefile-type) 'Makefile.in) + (error "Makefile.in is not supported")) + ((eq (oref this makefile-type) 'Makefile.am) + (require 'ede-pconf) + ;; Distribution variables + (let ((targ (if isdist (oref this targets) mt))) + (ede-compiler-begin-unique + (mapc 'ede-proj-makefile-insert-automake-pre-variables targ)) + (ede-compiler-begin-unique + (mapc 'ede-proj-makefile-insert-source-variables targ)) + (ede-compiler-begin-unique + (mapc 'ede-proj-makefile-insert-automake-post-variables targ)) + (ede-compiler-begin-unique + (ede-proj-makefile-insert-user-rules this)) + (insert "\n# End of Makefile.am\n") + (save-buffer)) + ) + (t (error "Unknown makefile type when generating Makefile"))) + ;; Put the cursor in a nice place + (goto-char (point-min))) + ;; If we have an original buffer, then don't kill it. + (when (not orig-buffer) + (kill-buffer buff-to-kill)) + )) + +;;; VARIABLE insertion +;; +(defun ede-pmake-end-of-variable () + "Move to the end of the variable declaration under point." + (end-of-line) + (while (= (preceding-char) ?\\) + (forward-char 1) + (end-of-line)) + ) + +(defmacro ede-pmake-insert-variable-shared (varname &rest body) + "Add VARNAME into the current Makefile. +Execute BODY in a location where a value can be placed." + `(let ((addcr t) (v ,varname)) + (if (re-search-backward (concat "^" v "\\s-*=") nil t) + (progn + (ede-pmake-end-of-variable) + (if (< (current-column) 40) + (if (and (/= (preceding-char) ?=) + (/= (preceding-char) ? )) + (insert " ")) + (insert "\\\n ")) + (setq addcr nil)) + (insert v "=")) + ,@body + (if addcr (insert "\n")) + (goto-char (point-max)))) +(put 'ede-pmake-insert-variable-shared 'lisp-indent-function 1) + +;;; SOURCE VARIABLE NAME CONSTRUCTION + +(defsubst ede-pmake-varname (obj) + "Convert OBJ into a variable name name. +Change . to _ in the variable name." + (let ((name (oref obj name))) + (while (string-match "\\." name) + (setq name (replace-match "_" nil t name))) + name)) + +(defmethod ede-proj-makefile-sourcevar ((this ede-proj-target)) + "Return the variable name for THIS's sources." + (concat (ede-pmake-varname this) "_YOU_FOUND_A_BUG")) + +;;; DEPENDENCY FILE GENERATOR LISTS +;; +(defmethod ede-proj-makefile-dependency-files ((this ede-proj-target)) + "Return a list of source files to convert to dependencies. +Argument THIS is the target to get sources from." + nil) + +;;; GENERIC VARIABLES +;; +(defmethod ede-proj-makefile-configuration-variables ((this ede-proj-project) + configuration) + "Return a list of configuration variables from THIS. +Use CONFIGURATION as the current configuration to query." + (cdr (assoc configuration (oref this configuration-variables)))) + +(defmethod ede-proj-makefile-insert-variables-new ((this ede-proj-project)) + "Insert variables needed by target THIS. + +NOTE: Not yet in use! This is part of an SRecode conversion of + EDE that is in progress." +; (let ((conf-table (ede-proj-makefile-configuration-variables +; this (oref this configuration-default))) +; (conf-done nil)) +; +; (ede-srecode-insert-with-dictionary +; "declaration:ede-vars" +; +; ;; Insert all variables, and augment them with details from +; ;; the current configuration. +; (mapc (lambda (c) +; +; (let ((ldict (srecode-dictionary-add-section-dictionary +; dict "VARIABLE")) +; ) +; (srecode-dictionary-set-value ldict "NAME" (car c)) +; (if (assoc (car c) conf-table) +; (let ((vdict (srecode-dictionary-add-section-dictionary +; ldict "VALUE"))) +; (srecode-dictionary-set-value +; vdict "VAL" (cdr (assoc (car c) conf-table))) +; (setq conf-done (cons (car c) conf-done)))) +; (let ((vdict (srecode-dictionary-add-section-dictionary +; ldict "VALUE"))) +; (srecode-dictionary-set-value vdict "VAL" (cdr c)))) +; ) +; +; (oref this variables)) +; +; ;; Add in all variables from the configuration not allready covered. +; (mapc (lambda (c) +; +; (if (member (car c) conf-done) +; nil +; (let* ((ldict (srecode-dictionary-add-section-dictionary +; dict "VARIABLE")) +; (vdict (srecode-dictionary-add-section-dictionary +; ldict "VALUE")) +; ) +; (srecode-dictionary-set-value ldict "NAME" (car c)) +; (srecode-dictionary-set-value vdict "VAL" (cdr c)))) +; ) +; +; conf-table) +; + + ;; @TODO - finish off this function, and replace the below fcn + +; )) + ) + +(defmethod ede-proj-makefile-insert-variables ((this ede-proj-project)) + "Insert variables needed by target THIS." + (let ((conf-table (ede-proj-makefile-configuration-variables + this (oref this configuration-default))) + (conf-done nil)) + ;; Insert all variables, and augment them with details from + ;; the current configuration. + (mapc (lambda (c) + (insert (car c) "=") + (if (assoc (car c) conf-table) + (progn + (insert (cdr (assoc (car c) conf-table)) " ") + (setq conf-done (cons (car c) conf-done)))) + (insert (cdr c) "\n")) + (oref this variables)) + ;; Add in all variables from the configuration not allready covered. + (mapc (lambda (c) + (if (member (car c) conf-done) + nil + (insert (car c) "=" (cdr c) "\n"))) + conf-table)) + (let* ((top "") + (tmp this)) + (while (ede-parent-project tmp) + (setq tmp (ede-parent-project tmp) + top (concat "../" top))) + (insert "\ntop=" top)) + (insert "\nede_FILES=" (file-name-nondirectory (oref this file)) " " + (file-name-nondirectory (ede-proj-dist-makefile this)) "\n")) + +(defmethod ede-proj-makefile-insert-source-variables ((this ede-proj-target) + &optional + moresource) + "Insert the source variables needed by THIS. +Optional argument MORESOURCE is a list of additional sources to add to the +sources variable." + (let ((sv (ede-proj-makefile-sourcevar this))) + ;; This variable may be shared between targets + (ede-pmake-insert-variable-shared (cond ((listp sv) (car sv)) + (t sv)) + (insert (mapconcat (lambda (a) a) (oref this source) " ")) + (if moresource + (insert " \\\n " (mapconcat (lambda (a) a) moresource " ") ""))))) + +(defmethod ede-proj-makefile-insert-variables ((this ede-proj-target) &optional + moresource) + "Insert variables needed by target THIS. +Optional argument MORESOURCE is a list of additional sources to add to the +sources variable." + (ede-proj-makefile-insert-source-variables this moresource) + ) + +(defmethod ede-proj-makefile-configuration-variables ((this ede-proj-target-makefile) + configuration) + "Return a list of configuration variables from THIS. +Use CONFIGURATION as the current configuration to query." + (cdr (assoc configuration (oref this configuration-variables)))) + +(defmethod ede-proj-makefile-insert-variables ((this ede-proj-target-makefile) + &optional moresource) + "Insert variables needed by target THIS. +Optional argument MORESOURCE is a list of additional sources to add to the +sources variable." + (call-next-method) + (let* ((proj (ede-target-parent this)) + (conf-table (ede-proj-makefile-configuration-variables + this (oref proj configuration-default))) + (conf-done nil) + ) + ;; Add in all variables from the configuration not allready covered. + (mapc (lambda (c) + (if (member (car c) conf-done) + nil + (insert (car c) "=" (cdr c) "\n"))) + conf-table)) + (let ((comp (ede-proj-compilers this)) + (link (ede-proj-linkers this)) + (name (ede-proj-makefile-target-name this)) + (src (oref this source))) + (while comp + (ede-compiler-only-once (car comp) + (ede-proj-makefile-insert-object-variables (car comp) name src) + (ede-proj-makefile-insert-variables (car comp))) + (setq comp (cdr comp))) + (while link + (ede-linker-only-once (car link) + (ede-proj-makefile-insert-variables (car link))) + (setq link (cdr link))))) + +(defmethod ede-proj-makefile-insert-automake-pre-variables + ((this ede-proj-target)) + "Insert variables needed by target THIS in Makefile.am before SOURCES." + nil) + +(defmethod ede-proj-makefile-insert-automake-post-variables + ((this ede-proj-target)) + "Insert variables needed by target THIS in Makefile.am after SOURCES." + nil) + +;;; GARBAGE PATTERNS +;; +(defmethod ede-proj-makefile-garbage-patterns ((this ede-proj-project)) + "Return a list of patterns that are considered garbage to THIS. +These are removed with make clean." + (let ((mc (ede-map-targets + this (lambda (c) (ede-proj-makefile-garbage-patterns c)))) + (uniq nil)) + (setq mc (sort (apply 'append mc) 'string<)) + ;; Filter out duplicates from the targets. + (while mc + (if (and (car uniq) (string= (car uniq) (car mc))) + nil + (setq uniq (cons (car mc) uniq))) + (setq mc (cdr mc))) + (nreverse uniq))) + +(defmethod ede-proj-makefile-garbage-patterns ((this ede-proj-target)) + "Return a list of patterns that are considered garbage to THIS. +These are removed with make clean." + ;; Get the the source object from THIS, and use the specified garbage. + (let ((src (ede-target-sourcecode this)) + (garb nil)) + (while src + (setq garb (append (oref (car src) garbagepattern) garb) + src (cdr src))) + garb)) + + +;;; RULES +;; +(defmethod ede-proj-makefile-insert-subproj-rules ((this ede-proj-project)) + "Insert a rule for the project THIS which should be a subproject." + (insert ".PHONY:" (ede-name this)) + (newline) + (insert (ede-name this) ":") + (newline) + (insert "\t$(MAKE) -C " (directory-file-name (ede-subproject-relative-path this))) + (newline) + (newline) + ) + +(defmethod ede-proj-makefile-insert-rules ((this ede-proj-project)) + "Insert rules needed by THIS target." + (mapc 'ede-proj-makefile-insert-rules (oref this inference-rules)) + ) + +(defmethod ede-proj-makefile-insert-dist-dependencies ((this ede-proj-project)) + "Insert any symbols that the DIST rule should depend on. +Argument THIS is the project that should insert stuff." + (mapc 'ede-proj-makefile-insert-dist-dependencies (oref this targets)) + ) + +(defmethod ede-proj-makefile-insert-dist-dependencies ((this ede-proj-target)) + "Insert any symbols that the DIST rule should depend on. +Argument THIS is the target that should insert stuff." + nil) + +(defmethod ede-proj-makefile-insert-dist-filepatterns ((this ede-proj-target)) + "Insert any symbols that the DIST rule should depend on. +Argument THIS is the target that should insert stuff." + (ede-proj-makefile-insert-dist-dependencies this) + ) + +(defmethod ede-proj-makefile-insert-dist-rules ((this ede-proj-project)) + "Insert distribution rules for THIS in a Makefile, such as CLEAN and DIST." + (let ((junk (ede-proj-makefile-garbage-patterns this)) + tmp) + ;; Build CLEAN, DIST, TAG, and other rules here. + (if junk + (insert "\nclean:\n" + "\trm -f " + (mapconcat (lambda (c) c) junk " ") + "\n\n")) + ;; @TODO: ^^^ Clean should also recurse. ^^^ + + (insert ".PHONY: dist\n") + (insert "\ndist:") + (ede-proj-makefile-insert-dist-dependencies this) + (insert "\n") + (unless (or (ede-subproject-p this) + (oref this metasubproject)) + ;; Only delete if we are the toplevel project. + (insert "\trm -rf $(DISTDIR)\n")) + (insert "\tmkdir $(DISTDIR)\n") ;We may need a -p, but I think not. + (setq tmp (oref this targets)) + (insert "\tcp") + (while tmp + (let ((sv (ede-proj-makefile-sourcevar (car tmp)))) + (if (listp sv) + ;; Handle special case variables. + (cond ((eq (cdr sv) 'share) + ;; This variable may be shared between multiple targets. + (if (re-search-backward (concat "\\$(" (car sv) ")") + (save-excursion + (beginning-of-line) + (point)) + t) + ;; If its already in the dist target, then skip it. + nil + (setq sv (car sv)))) + (t (setq sv (car sv))))) + (if (stringp sv) + (insert " $(" sv ")")) + (ede-proj-makefile-insert-dist-filepatterns (car tmp)) + (setq tmp (cdr tmp)))) + (insert " $(ede_FILES) $(DISTDIR)\n") + + ;; Call our sub projects. + (ede-map-subprojects + this (lambda (sproj) + (let ((rp (directory-file-name (ede-subproject-relative-path sproj)))) + (insert "\t$(MAKE) -C " rp " $(MFLAGS) DISTDIR=$(DISTDIR)/" rp + " dist" + "\n")))) + + ;; Tar up the stuff. + (unless (or (ede-subproject-p this) + (oref this metasubproject)) + (insert "\ttar -cvzf $(DISTDIR).tar.gz $(DISTDIR)\n" + "\trm -rf $(DISTDIR)\n")) + + ;; Make sure the Makefile is ok. + (insert "\n" + (file-name-nondirectory (buffer-file-name)) ": " + (file-name-nondirectory (oref this file)) "\n" +;; "$(EMACS) -batch Project.ede -l ede -f ede-proj-regenerate" + "\t@echo Makefile is out of date! " + "It needs to be regenerated by EDE.\n" + "\t@echo If you have not modified Project.ede, you can" + " use 'touch' to update the Makefile time stamp.\n" + "\t@false\n\n" + "\n\n# End of Makefile\n"))) + +(defmethod ede-proj-makefile-insert-rules ((this ede-proj-target)) + "Insert rules needed by THIS target." + nil) + +(defmethod ede-proj-makefile-insert-rules ((this ede-proj-target-makefile)) + "Insert rules needed by THIS target." + (mapc 'ede-proj-makefile-insert-rules (oref this rules)) + (let ((c (ede-proj-compilers this))) + (when c + (mapc 'ede-proj-makefile-insert-rules c) + (if (oref this phony) + (insert ".PHONY: " (ede-proj-makefile-target-name this) "\n")) + (insert (ede-proj-makefile-target-name this) ": " + (ede-proj-makefile-dependencies this) "\n") + (ede-proj-makefile-insert-commands this) + ))) + +(defmethod ede-proj-makefile-insert-commands ((this ede-proj-target-makefile)) + "Insert the commands needed by target THIS. +For targets, insert the commands needed by the chosen compiler." + (mapc 'ede-proj-makefile-insert-commands (ede-proj-compilers this)) + (when (object-assoc t :uselinker (ede-proj-compilers this)) + (mapc 'ede-proj-makefile-insert-commands (ede-proj-linkers this)))) + + +(defmethod ede-proj-makefile-insert-user-rules ((this ede-proj-project)) + "Insert user specified rules needed by THIS target. +This is different from `ede-proj-makefile-insert-rules' in that this +function won't create the building rules which are auto created with +automake." + (mapc 'ede-proj-makefile-insert-user-rules (oref this inference-rules))) + +(defmethod ede-proj-makefile-insert-user-rules ((this ede-proj-target)) + "Insert user specified rules needed by THIS target." + (mapc 'ede-proj-makefile-insert-rules (oref this rules))) + +(defmethod ede-proj-makefile-dependencies ((this ede-proj-target-makefile)) + "Return a string representing the dependencies for THIS. +Some compilers only use the first element in the dependencies, others +have a list of intermediates (object files), and others don't care. +This allows customization of how these elements appear." + (let* ((c (ede-proj-compilers this)) + (io (ede-or (mapcar 'ede-compiler-intermediate-objects-p c))) + (out nil)) + (if io + (progn + (while c + (setq out + (concat out "$(" (ede-compiler-intermediate-object-variable + (car c) + (ede-proj-makefile-target-name this)) ")") + c (cdr c))) + out) + (let ((sv (ede-proj-makefile-sourcevar this)) + (aux (oref this auxsource))) + (setq out + (if (and (stringp sv) (not (string= sv ""))) + (concat "$(" sv ")") + "")) + (while aux + (setq out (concat out " " (car aux))) + (setq aux (cdr aux))) + out)))) + +;; Tags +(defmethod ede-proj-makefile-tags ((this ede-proj-project) targets) + "Insert into the current location rules to make recursive TAGS files. +Argument THIS is the project to create tags for. +Argument TARGETS are the targets we should depend on for TAGS." + (insert "tags: ") + (let ((tg targets)) + ;; Loop over all source variables and insert them + (while tg + (insert "$(" (ede-proj-makefile-sourcevar (car tg)) ") ") + (setq tg (cdr tg))) + (insert "\n") + (if targets + (insert "\tetags $^\n")) + ;; Now recurse into all subprojects + (setq tg (oref this subproj)) + (while tg + (insert "\t$(MAKE) -C " (ede-subproject-relative-path (car tg)) " $(MFLAGS) $@\n") + (setq tg (cdr tg))) + (insert "\n"))) + + +(provide 'ede/pmake) + +;;; ede/pmake.el ends here diff --git a/lisp/cedet/ede/proj-archive.el b/lisp/cedet/ede/proj-archive.el new file mode 100644 index 00000000000..b19fc3015df --- /dev/null +++ b/lisp/cedet/ede/proj-archive.el @@ -0,0 +1,64 @@ +;;; ede/proj-archive.el --- EDE Generic Project archive support + +;;; Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Keywords: project, make + +;; 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: +;; +;; Handle object code archives in and EDE Project file. + +(require 'ede/pmake) +(require 'ede/proj-obj) + +;;; Code: + +(defclass ede-proj-target-makefile-archive + (ede-proj-target-makefile-objectcode) + ((availablelinkers :initform (ede-archive-linker))) + "This target generates an object code archive.") + +(defvar ede-archive-linker + (ede-linker + "ede-archive-linker" + :name "ar" + :variables '(("AR" . "ar") + ("AR_CMD" . "$(AR) cr")) + :commands '("$(AR_CMD) lib$@.a $^") + :autoconf '(("AC_CHECK_PROGS" . "RANLIB, ranlib")) + :objectextention "") + "Linker object for creating an archive.") + +(defmethod ede-proj-makefile-insert-source-variables :BEFORE + ((this ede-proj-target-makefile-archive) &optional moresource) + "Insert bin_PROGRAMS variables needed by target THIS. +We aren't acutally inserting SOURCE details, but this is used by the +Makefile.am generator, so use it to add this important bin program." + (ede-pmake-insert-variable-shared + (concat "lib" (ede-name this) "_a_LIBRARIES") + (insert (concat "lib" (ede-name this) ".a")))) + +(defmethod ede-proj-makefile-garbage-patterns + ((this ede-proj-target-makefile-archive)) + "Add archive name to the garbage patterns. +This makes sure that the archive is removed with 'make clean'." + (let ((garb (call-next-method))) + (append garb (list (concat "lib" (ede-name this) ".a"))))) + +(provide 'ede/proj-archive) + +;;; ede/proj-archive.el ends here diff --git a/lisp/cedet/ede/proj-aux.el b/lisp/cedet/ede/proj-aux.el new file mode 100644 index 00000000000..3cb0b7c029d --- /dev/null +++ b/lisp/cedet/ede/proj-aux.el @@ -0,0 +1,47 @@ +;;; ede/proj-aux.el --- EDE Generic Project auxilliary file support + +;;; Copyright (C) 1998, 1999, 2000, 2007 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: +;; +;; Handle auxiliary files (README, FAQ, etc) in and EDE Project file. + +(require 'ede/proj) +(require 'ede/pmake) + +;;; Code: +(defclass ede-proj-target-aux (ede-proj-target) + ((sourcetype :initform (ede-aux-source))) + "This target consists of aux files such as READMEs and COPYING.") + +(defvar ede-aux-source + (ede-sourcecode "ede-aux-source-txt" + :name "Auxiliary Text" + :sourcepattern "^[A-Z]+$\\|\\.txt$") + "Miscelaneous fields definition.") + +(defmethod ede-proj-makefile-sourcevar ((this ede-proj-target-aux)) + "Return the variable name for THIS's sources." + (concat (ede-pmake-varname this) "_AUX")) + +(provide 'ede/proj-aux) + +;;; ede/proj-aux.el ends here diff --git a/lisp/cedet/ede/proj-comp.el b/lisp/cedet/ede/proj-comp.el new file mode 100644 index 00000000000..90b65ea8a8e --- /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 diff --git a/lisp/cedet/ede/proj-elisp.el b/lisp/cedet/ede/proj-elisp.el new file mode 100644 index 00000000000..068daae44de --- /dev/null +++ b/lisp/cedet/ede/proj-elisp.el @@ -0,0 +1,393 @@ +;;; ede-proj-elisp.el --- EDE Generic Project Emacs Lisp support + +;;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, +;;; 2007, 2008, 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: +;; +;; Handle Emacs Lisp in and EDE Project file. + +(require 'ede/proj) +(require 'ede/pmake) +(require 'ede/pconf) + +;;; Code: +(defclass ede-proj-target-elisp (ede-proj-target-makefile) + ((menu :initform nil) + (keybindings :initform nil) + (phony :initform t) + (sourcetype :initform (ede-source-emacs)) + (availablecompilers :initform (ede-emacs-compiler ede-xemacs-compiler)) + (aux-packages :initarg :aux-packages + :initform nil + :type list + :custom (repeat string) + :documentation "Additional packages needed. +There should only be one toplevel package per auxiliary tool needed. +These packages location is found, and added to the compile time +load path." + )) + "This target consists of a group of lisp files. +A lisp target may be one general program with many separate lisp files in it.") + +(defvar ede-source-emacs + (ede-sourcecode "ede-emacs-source" + :name "Emacs Lisp" + :sourcepattern "\\.el$" + :garbagepattern '("*.elc")) + "Emacs Lisp source code definition.") + +(defvar ede-emacs-compiler + (ede-compiler + "ede-emacs-compiler" + :name "emacs" + :variables '(("EMACS" . "emacs") + ("EMACSFLAGS" . "-batch --no-site-file")) + :commands + '("@echo \"(add-to-list 'load-path nil)\" > $@-compile-script" + "for loadpath in . ${LOADPATH}; do \\" + " echo \"(add-to-list 'load-path \\\"$$loadpath\\\")\" >> $@-compile-script; \\" + "done;" + "@echo \"(setq debug-on-error t)\" >> $@-compile-script" + "\"$(EMACS)\" $(EMACSFLAGS) -l $@-compile-script -f batch-byte-compile $^" + ) + :autoconf '("AM_PATH_LISPDIR") + :sourcetype '(ede-source-emacs) +; :objectextention ".elc" + ) + "Compile Emacs Lisp programs.") + +(defvar ede-xemacs-compiler + (clone ede-emacs-compiler "ede-xemacs-compiler" + :name "xemacs" + :variables '(("EMACS" . "xemacs"))) + "Compile Emacs Lisp programs with XEmacs.") + +;;; Claiming files +(defmethod ede-buffer-mine ((this ede-proj-target-elisp) buffer) + "Return t if object THIS lays claim to the file in BUFFER. +Lays claim to all .elc files that match .el files in this target." + (if (string-match "\\.elc$" (buffer-file-name buffer)) + (let ((fname + (concat + (file-name-sans-extension (buffer-file-name buffer)) + ".el") + )) + ;; Is this in our list. + (member fname (oref this auxsource)) + ) + (call-next-method) ; The usual thing. + )) + +;;; Emacs Lisp Compiler +;;; Emacs Lisp Target +(defun ede-proj-elisp-packages-to-loadpath (packages) + "Convert a list of PACKAGES, to a list of load path." + (let ((paths nil) + (ldir nil)) + (while packages + (or (setq ldir (locate-library (car packages))) + (error "Cannot find package %s" (car packages))) + (let* ((fnd (file-name-directory ldir)) + (rel (file-relative-name fnd)) + (full nil) + ) + ;; Make sure the relative name isn't to far off + (when (string-match "^\\.\\./\\.\\./\\.\\./\\.\\." rel) + (setq full fnd)) + ;; Do the setup. + (setq paths (cons (or full rel) paths) + packages (cdr packages)))) + paths)) + +(defmethod project-compile-target ((obj ede-proj-target-elisp)) + "Compile all sources in a Lisp target OBJ. +Bonus: Return a cons cell: (COMPILED . UPTODATE)." + (let* ((proj (ede-target-parent obj)) + (dir (oref proj directory)) + (comp 0) + (utd 0)) + (mapc (lambda (src) + (let* ((fsrc (expand-file-name src dir)) + (elc (concat (file-name-sans-extension fsrc) ".elc")) + ) + (if (or (not (file-exists-p elc)) + (file-newer-than-file-p fsrc elc)) + (progn + (setq comp (1+ comp)) + (byte-compile-file fsrc)) + (setq utd (1+ utd))))) + (oref obj source)) + (message "All Emacs Lisp sources are up to date in %s" (object-name obj)) + (cons comp utd) + )) + +(defmethod ede-update-version-in-source ((this ede-proj-target-elisp) version) + "In a Lisp file, updated a version string for THIS to VERSION. +There are standards in Elisp files specifying how the version string +is found, such as a `-version' variable, or the standard header." + (if (and (slot-boundp this 'versionsource) + (oref this versionsource)) + (let ((vs (oref this versionsource)) + (match nil)) + (while vs + (save-excursion + (set-buffer (find-file-noselect + (ede-expand-filename this (car vs)))) + (goto-char (point-min)) + (let ((case-fold-search t)) + (if (re-search-forward "-version\\s-+\"\\([^\"]+\\)\"" nil t) + (progn + (setq match t) + (delete-region (match-beginning 1) + (match-end 1)) + (goto-char (match-beginning 1)) + (insert version))))) + (setq vs (cdr vs))) + (if (not match) (call-next-method))))) + + +;;; Makefile generation functions +;; +(defmethod ede-proj-makefile-sourcevar ((this ede-proj-target-elisp)) + "Return the variable name for THIS's sources." + (cond ((ede-proj-automake-p) '("lisp_LISP" . share)) + (t (concat (ede-pmake-varname this) "_LISP")))) + +(defun ede-proj-makefile-insert-loadpath-items (items) + "Insert a sequence of ITEMS into the Makefile LOADPATH variable." + (when items + (ede-pmake-insert-variable-shared "LOADPATH" + (let ((begin (save-excursion (re-search-backward "\\s-*=")))) + (while items + (when (not (save-excursion + (re-search-backward + (concat "\\s-" (regexp-quote (car items)) "[ \n\t\\]") + begin t))) + (insert " " (car items))) + (setq items (cdr items))))) + )) + +(defmethod ede-proj-makefile-insert-variables :AFTER ((this ede-proj-target-elisp)) + "Insert variables needed by target THIS." + (let ((newitems (if (oref this aux-packages) + (ede-proj-elisp-packages-to-loadpath + (oref this aux-packages)))) + ) + (ede-proj-makefile-insert-loadpath-items newitems))) + +(defun ede-proj-elisp-add-path (path) + "Add path PATH into the file if it isn't already there." + (goto-char (point-min)) + (if (re-search-forward (concat "(cons \\\"" + (regexp-quote path)) + nil t) + nil;; We have it already + (if (re-search-forward "(cons nil" nil t) + (progn + ;; insert stuff here + (end-of-line) + (insert "\n" + " echo \"(setq load-path (cons \\\"" + path + "\\\" load-path))\" >> script") + ) + (error "Don't know how to update load path")))) + +(defmethod ede-proj-tweak-autoconf ((this ede-proj-target-elisp)) + "Tweak the configure file (current buffer) to accomodate THIS." + (call-next-method) + ;; Ok, now we have to tweak the autoconf provided `elisp-comp' program. + (let ((ec (ede-expand-filename this "elisp-comp" 'newfile))) + (if (or (not ec) (not (file-exists-p ec))) + (message "No elisp-comp file. There may be compile errors? Rerun a second time.") + (save-excursion + (if (file-symlink-p ec) + (progn + ;; Desymlinkafy + (rename-file ec (concat ec ".tmp")) + (copy-file (concat ec ".tmp") ec) + (delete-file (concat ec ".tmp")))) + (set-buffer (find-file-noselect ec t)) + (ede-proj-elisp-add-path "..") + (let ((paths (ede-proj-elisp-packages-to-loadpath + (oref this aux-packages)))) + ;; Add in the current list of paths + (while paths + (ede-proj-elisp-add-path (car paths)) + (setq paths (cdr paths)))) + (save-buffer)) ))) + +(defmethod ede-proj-flush-autoconf ((this ede-proj-target-elisp)) + "Flush the configure file (current buffer) to accomodate THIS." + ;; Remove crufty old paths from elisp-compile + (let ((ec (ede-expand-filename this "elisp-comp" 'newfile)) + ) + (if (and ec (file-exists-p ec)) + (save-excursion + (set-buffer (find-file-noselect ec t)) + (goto-char (point-min)) + (while (re-search-forward "(cons \\([^ ]+\\) load-path)" + nil t) + (let ((path (match-string 1))) + (if (string= path "nil") + nil + (delete-region (save-excursion (beginning-of-line) (point)) + (save-excursion (end-of-line) + (forward-char 1) + (point)))))))))) + +;;; +;; Autoload generators +;; +(defclass ede-proj-target-elisp-autoloads (ede-proj-target-elisp) + ((availablecompilers :initform (ede-emacs-cedet-autogen-compiler)) + (aux-packages :initform ("cedet-autogen")) + (phony :initform t) + (autoload-file :initarg :autoload-file + :initform "loaddefs.el" + :type string + :custom string + :documentation "The file that autoload definitions are placed in. +There should be one load defs file for a given package. The load defs are created +for all Emacs Lisp sources that exist in the directory of the created target.") + (autoload-dirs :initarg :autoload-dirs + :initform nil + :type list + :custom (repeat string) + :documentation "The directories to scan for autoload definitions. +If nil defaults to the current directory.") + ) + "Target that builds an autoload file. +Files do not need to be added to this target.") + + +;;; Claiming files +(defmethod ede-buffer-mine ((this ede-proj-target-elisp-autoloads) buffer) + "Return t if object THIS lays claim to the file in BUFFER. +Lays claim to all .elc files that match .el files in this target." + (if (string-match + (concat (regexp-quote (oref this autoload-file)) "$") + (buffer-file-name buffer)) + t + (call-next-method) ; The usual thing. + )) + +;; Compilers +(defvar ede-emacs-cedet-autogen-compiler + (ede-compiler + "ede-emacs-autogen-compiler" + :name "emacs" + :variables '(("EMACS" . "emacs")) + :commands + '("@echo \"(add-to-list 'load-path nil)\" > $@-compile-script" + "for loadpath in . ${LOADPATH}; do \\" + " echo \"(add-to-list 'load-path \\\"$$loadpath\\\")\" >> $@-compile-script; \\" + "done;" + "@echo \"(require 'cedet-autogen)\" >> $@-compile-script" + "\"$(EMACS)\" -batch --no-site-file -l $@-compile-script -f cedet-batch-update-autoloads $(LOADDEFS) $(LOADDIRS)" + ) + :sourcetype '(ede-source-emacs) + ) + "Build an autoloads file.") + +(defmethod ede-proj-compilers ((obj ede-proj-target-elisp-autoloads)) + "List of compilers being used by OBJ. +If the `compiler' slot is empty, get the car of the compilers list." + (let ((comp (oref obj compiler))) + (if comp + (if (listp comp) + (setq comp (mapcar 'symbol-value comp)) + (setq comp (list (symbol-value comp)))) + ;; Get the first element from our list of compilers. + (let ((avail (mapcar 'symbol-value (oref obj availablecompilers)))) + (setq comp (list (car avail))))) + comp)) + +(defmethod ede-proj-makefile-insert-source-variables ((this ede-proj-target-elisp-autoloads) + &optional + moresource) + "Insert the source variables needed by THIS. +Optional argument MORESOURCE is a list of additional sources to add to the +sources variable." + nil) + +(defmethod ede-proj-makefile-sourcevar ((this ede-proj-target-elisp-autoloads)) + "Return the variable name for THIS's sources." + nil) ; "LOADDEFS") + +(defmethod ede-proj-makefile-dependencies ((this ede-proj-target-elisp-autoloads)) + "Return a string representing the dependencies for THIS. +Always return an empty string for an autoloads generator." + "") + +(defmethod ede-proj-makefile-insert-variables :AFTER ((this ede-proj-target-elisp-autoloads)) + "Insert variables needed by target THIS." + (ede-pmake-insert-variable-shared "LOADDEFS" + (insert (oref this autoload-file))) + (ede-pmake-insert-variable-shared "LOADDIRS" + (insert (mapconcat 'identity + (or (oref this autoload-dirs) '(".")) + " "))) + ) + +(defmethod project-compile-target ((obj ede-proj-target-elisp-autoloads)) + "Create or update the autoload target." + (require 'cedet-autogen) + (let ((default-directory (ede-expand-filename obj "."))) + (apply 'cedet-update-autoloads + (oref obj autoload-file) + (oref obj autoload-dirs)) + )) + +(defmethod ede-update-version-in-source ((this ede-proj-target-elisp-autoloads) version) + "In a Lisp file, updated a version string for THIS to VERSION. +There are standards in Elisp files specifying how the version string +is found, such as a `-version' variable, or the standard header." + nil) + +(defmethod ede-proj-makefile-insert-dist-dependencies ((this ede-proj-target-elisp-autoloads)) + "Insert any symbols that the DIST rule should depend on. +Emacs Lisp autoload files ship the generated .el files. +Argument THIS is the target which needs to insert an info file." + ;; In some cases, this is ONLY the index file. That should generally + ;; be ok. + (insert " " (ede-proj-makefile-target-name this)) + ) + +(defmethod ede-proj-makefile-insert-dist-filepatterns ((this ede-proj-target-elisp-autoloads)) + "Insert any symbols that the DIST rule should distribute. +Emacs Lisp autoload files ship the generated .el files. +Argument THIS is the target which needs to insert an info file." + (insert " " (oref this autoload-file)) + ) + +(defmethod ede-proj-tweak-autoconf ((this ede-proj-target-elisp-autoloads)) + "Tweak the configure file (current buffer) to accomodate THIS." + (error "Autoloads not supported in autoconf yet.")) + +(defmethod ede-proj-flush-autoconf ((this ede-proj-target-elisp-autoloads)) + "Flush the configure file (current buffer) to accomodate THIS." + nil) + +(provide 'ede/proj-elisp) + +;;; ede/proj-elisp.el ends here diff --git a/lisp/cedet/ede/proj-info.el b/lisp/cedet/ede/proj-info.el new file mode 100644 index 00000000000..215f98e09e2 --- /dev/null +++ b/lisp/cedet/ede/proj-info.el @@ -0,0 +1,186 @@ +;;; ede-proj-info.el --- EDE Generic Project texinfo support + +;;; Copyright (C) 1998, 1999, 2000, 2001, 2004, 2007, 2008, 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: +;; +;; Handle texinfo in and EDE Project file. + +(require 'ede/pmake) + +;;; Code: +(defclass ede-proj-target-makefile-info (ede-proj-target-makefile) + ((menu :initform nil) + (keybindings :initform nil) + (availablecompilers :initform (ede-makeinfo-compiler + ede-texi2html-compiler)) + (sourcetype :initform (ede-makeinfo-source)) + (mainmenu :initarg :mainmenu + :initform "" + :type string + :custom string + :documentation "The main menu resides in this file. +All other sources should be included independently.")) + "Target for a single info file.") + +(defvar ede-makeinfo-source + (ede-sourcecode "ede-makeinfo-source" + :name "Texinfo" + :sourcepattern "\\.texi?$" + :garbagepattern '("*.info*" "*.html")) + "Texinfo source code definition.") + +(defvar ede-makeinfo-compiler + (ede-compiler + "ede-makeinfo-compiler" + :name "makeinfo" + :variables '(("MAKEINFO" . "makeinfo")) + :commands '("$(MAKEINFO) $<") + :autoconf '(("AC_CHECK_PROG" . "MAKEINFO, makeinfo")) + :sourcetype '(ede-makeinfo-source) + ) + "Compile texinfo files into info files.") + +(defvar ede-texi2html-compiler + (ede-compiler + "ede-texi2html-compiler" + :name "texi2html" + :variables '(("TEXI2HTML" . "makeinfo -html")) + :commands '("makeinfo -o $@ $<") + :sourcetype '(ede-makeinfo-source) + ) + "Compile texinfo files into html files.") + +;;; Makefile generation +;; +(defmethod ede-proj-configure-add-missing + ((this ede-proj-target-makefile-info)) + "Query if any files needed by THIS provided by automake are missing. +Results in --add-missing being passed to automake." + (not (ede-expand-filename (ede-toplevel) "texinfo.tex"))) + +(defmethod ede-proj-makefile-sourcevar ((this ede-proj-target-makefile-info)) + "Return the variable name for THIS's sources." + (concat (ede-pmake-varname this) "_TEXINFOS")) + +(defmethod ede-proj-makefile-insert-source-variables + ((this ede-proj-target-makefile-info) &optional moresource) + "Insert the source variables needed by THIS info target. +Optional argument MORESOURCE is a list of additional sources to add to the +sources variable. +Does the usual for Makefile mode, but splits source into two variables +when working in Automake mode." + (if (not (ede-proj-automake-p)) + (call-next-method) + (let* ((sv (ede-proj-makefile-sourcevar this)) + (src (copy-sequence (oref this source))) + (menu (or (oref this menu) (car src)))) + (setq src (delq menu src)) + ;; the info_TEXINFOS variable is probably shared + (ede-pmake-insert-variable-shared "info_TEXINFOS" + (insert menu)) + ;; Now insert the rest of the source elsewhere + (ede-pmake-insert-variable-shared sv + (insert (mapconcat 'identity src " "))) + (if moresource + (error "Texinfo files should not have moresource"))))) + +(defun ede-makeinfo-find-info-filename (source) + "Find the info filename produced by SOURCE texinfo file." + (let ((opened (get-file-buffer source)) + (buffer (or (get-file-buffer source) + (find-file-noselect source nil t))) + info) + (with-current-buffer buffer + (save-excursion + (goto-char (point-min)) + (and (re-search-forward "^@setfilename\\s-+\\([^.]+\\).info$" nil t) + (setq info (match-string 1))))) + (unless (eq buffer opened) + (kill-buffer buffer)) + info)) + +(defmethod ede-proj-makefile-target-name ((this ede-proj-target-makefile-info)) + "Return the name of the main target for THIS target." + ;; The target should be the main-menu file name translated to .info. + (let* ((source (if (not (string= (oref this mainmenu) "")) + (oref this mainmenu) + (car (oref this source)))) + (info (ede-makeinfo-find-info-filename source))) + (concat (or info (file-name-sans-extension source)) ".info"))) + +(defmethod ede-proj-makefile-insert-dist-dependencies ((this ede-proj-target-makefile-info)) + "Insert any symbols that the DIST rule should depend on. +Texinfo files want to insert generated `.info' files. +Argument THIS is the target which needs to insert an info file." + ;; In some cases, this is ONLY the index file. That should generally + ;; be ok. + (insert " " (ede-proj-makefile-target-name this)) + ) + +(defmethod ede-proj-makefile-insert-dist-filepatterns ((this ede-proj-target-makefile-info)) + "Insert any symbols that the DIST rule should depend on. +Texinfo files want to insert generated `.info' files. +Argument THIS is the target which needs to insert an info file." + ;; In some cases, this is ONLY the index file. That should generally + ;; be ok. + (insert " " (ede-proj-makefile-target-name this) "*") + ) + +; (let ((n (ede-name this))) +; (if (string-match "\\.info$" n) +; n +; (concat n ".info")))) + +(defmethod object-write ((this ede-proj-target-makefile-info)) + "Before committing any change to THIS, make sure the mainmenu is first." + (let ((mm (oref this mainmenu)) + (s (oref this source)) + (nl nil)) + (if (or (string= mm "") (not mm) (string= mm (car s))) + nil + ;; Make sure that MM is first in the list of items. + (setq nl (cons mm (delq mm s))) + (oset this source nl))) + (call-next-method)) + +(defmethod ede-documentation ((this ede-proj-target-makefile-info)) + "Return a list of files that provides documentation. +Documentation is not for object THIS, but is provided by THIS for other +files in the project." + (let* ((src (oref this source)) + (proj (ede-target-parent this)) + (dir (oref proj directory)) + (out nil) + ) + ;; convert src to full file names. + (while src + (setq out (cons + (expand-file-name (car src) dir) + out)) + (setq src (cdr src))) + ;; Return it + out)) + +(provide 'ede/proj-info) + +;;; ede/proj-info.el ends here diff --git a/lisp/cedet/ede/proj-misc.el b/lisp/cedet/ede/proj-misc.el new file mode 100644 index 00000000000..0674989e221 --- /dev/null +++ b/lisp/cedet/ede/proj-misc.el @@ -0,0 +1,93 @@ +;;; ede-proj-nusc.el --- EDE Generic Project Emacs Lisp support + +;;; Copyright (C) 1998, 1999, 2000, 2001, 2008 +;;; 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: +;; +;; Handle miscelaneous compilable projects in and EDE Project file. +;; This misc target lets the user link in custom makefiles to an EDE +;; project. + +(require 'ede/pmake) +(require 'ede/proj-comp) + +;;; Code: +(defclass ede-proj-target-makefile-miscelaneous (ede-proj-target-makefile) + ((sourcetype :initform (ede-misc-source)) + (availablecompilers :initform (ede-misc-compile)) + (submakefile :initarg :submakefile + :initform "" + :type string + :custom string + :documentation + "Miscellaneous sources which have a specialized makefile. +The sub-makefile is used to build this target.") + ) + "Miscelaneous target type. +A user-written makefile is used to build this target. +All listed sources are included in the distribution.") + +(defvar ede-misc-source + (ede-sourcecode "ede-misc-source" + :name "Miscelaneous" + :sourcepattern ".*") + "Miscelaneous fiels definition.") + +(defvar ede-misc-compile + (ede-compiler "ede-misc-compile" + :name "Sub Makefile" + :commands + '( + ) + :autoconf nil + :sourcetype '(ede-misc-source) + ) + "Compile code via a sub-makefile.") + +(defmethod ede-proj-makefile-sourcevar ((this ede-proj-target-makefile-miscelaneous)) + "Return the variable name for THIS's sources." + (concat (ede-pmake-varname this) "_MISC")) + +(defmethod ede-proj-makefile-dependency-files + ((this ede-proj-target-makefile-miscelaneous)) + "Return a list of files which THIS target depends on." + (with-slots (submakefile) this + (cond ((string= submakefile "") + nil) + ((not submakefile) + nil) + (t (list submakefile))))) + +(defmethod ede-proj-makefile-insert-rules ((this ede-proj-target-makefile-miscelaneous)) + "Create the make rule needed to create an archive for THIS." + ;; DO NOT call the next method. We will never have any compilers, + ;; or any dependencies, or stuff like this. This rull will lets us + ;; deal with it in a nice way. + (insert (ede-name this) ": ") + (with-slots (submakefile) this + (if (string= submakefile "") + (insert "\n\t@\n\n") + (insert submakefile "\n" "\t$(MAKE) -f " submakefile "\n\n")))) + +(provide 'ede/proj-misc) + +;;; ede/proj-misc.el ends here diff --git a/lisp/cedet/ede/proj-obj.el b/lisp/cedet/ede/proj-obj.el new file mode 100644 index 00000000000..a42646a5f31 --- /dev/null +++ b/lisp/cedet/ede/proj-obj.el @@ -0,0 +1,281 @@ +;;; ede/proj-obj.el --- EDE Generic Project Object code generation support + +;;; Copyright (C) 1998, 1999, 2000, 2005, 2008, 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: +;; +;; Handles a supperclass of target types which create object code in +;; and EDE Project file. + +(require 'ede/proj) +(declare-function ede-pmake-varname "ede/pmake") + +(defvar ede-proj-objectcode-dodependencies nil + "Flag specifies to do automatic dependencies.") + +;;; Code: +(defclass ede-proj-target-makefile-objectcode (ede-proj-target-makefile) + (;; Give this a new default + (configuration-variables :initform ("debug" . (("CFLAGS" . "-g") + ("LDFLAGS" . "-g")))) + ;; @TODO - add an include path. + (availablecompilers :initform (ede-gcc-compiler + ede-g++-compiler + ede-gfortran-compiler + ede-gfortran-module-compiler + ;; More C and C++ compilers, plus + ;; fortran or pascal can be added here + )) + (availablelinkers :initform (ede-g++-linker + ;; Add more linker thingies here. + ede-ld-linker + ede-gfortran-linker + )) + (sourcetype :initform (ede-source-c + ede-source-c++ + ede-source-f77 + ede-source-f90 + ;; ede-source-other + ;; This object should take everything that + ;; gets compiled into objects like fortran + ;; and pascal. + )) + ) + "Abstract class for Makefile based object code generating targets. +Belonging to this group assumes you could make a .o from an element source +file.") + +(defclass ede-object-compiler (ede-compiler) + ((uselinker :initform t) + (dependencyvar :initarg :dependencyvar + :type list + :custom (cons (string :tag "Variable") + (string :tag "Value")) + :documentation + "A variable dedicated to dependency generation.")) + "Ede compiler class for source which must compiler, and link.") + +;;; C/C++ Compilers and Linkers +;; +(defvar ede-source-c + (ede-sourcecode "ede-source-c" + :name "C" + :sourcepattern "\\.c$" + :auxsourcepattern "\\.h$" + :garbagepattern '("*.o" "*.obj" ".deps/*.P" ".lo")) + "C source code definition.") + +(defvar ede-gcc-compiler + (ede-object-compiler + "ede-c-compiler-gcc" + :name "gcc" + :dependencyvar '("C_DEPENDENCIES" . "-Wp,-MD,.deps/$(*F).P") + :variables '(("CC" . "gcc") + ("C_COMPILE" . + "$(CC) $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)")) + :rules (list (ede-makefile-rule + "c-inference-rule" + :target "%.o" + :dependencies "%.c" + :rules '("@echo '$(C_COMPILE) -c $<'; \\" + "$(C_COMPILE) $(C_DEPENDENCIES) -o $@ -c $<" + ) + )) + :autoconf '("AC_PROG_CC" "AC_PROG_GCC_TRADITIONAL") + :sourcetype '(ede-source-c) + :objectextention ".o" + :makedepends t + :uselinker t) + "Compiler for C sourcecode.") + +(defvar ede-source-c++ + (ede-sourcecode "ede-source-c++" + :name "C++" + :sourcepattern "\\.\\(cpp\\|cc\\|cxx\\)$" + :auxsourcepattern "\\.\\(hpp\\|hh?\\|hxx\\)$" + :garbagepattern '("*.o" "*.obj" ".deps/*.P" ".lo")) + "C++ source code definition.") + +(defvar ede-g++-compiler + (ede-object-compiler + "ede-c-compiler-g++" + :name "g++" + :dependencyvar '("CXX_DEPENDENCIES" . "-Wp,-MD,.deps/$(*F).P") + :variables '(("CXX" "g++") + ("CXX_COMPILE" . + "$(CXX) $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)") + ) + :rules (list (ede-makefile-rule + "c++-inference-rule" + :target "%.o" + :dependencies "%.cpp" + :rules '("@echo '$(CXX_COMPILE) -c $<'; \\" + "$(CXX_COMPILE) $(CXX_DEPENDENCIES) -o $@ -c $<" + ) + )) + :autoconf '("AC_PROG_CXX") + :sourcetype '(ede-source-c++) + :objectextention ".o" + :makedepends t + :uselinker t) + "Compiler for C sourcecode.") + +(defvar ede-g++-linker + (ede-linker + "ede-g++-linker" + :name "g++" + ;; Only use this linker when c++ exists. + :sourcetype '(ede-source-c++) + :variables '(("CXX_LINK" . + "$(CXX) $(CFLAGS) $(LDFLAGS) -L. -o $@") + ) + :commands '("$(CXX_LINK) $^") + :autoconf '("AC_PROG_CXX") + :objectextention "") + "Linker needed for c++ programs.") + +;;; Fortran Compiler/Linker +;; +;; Contributed by David Engster +(defvar ede-source-f90 + (ede-sourcecode "ede-source-f90" + :name "Fortran 90/95" + :sourcepattern "\\.[fF]9[05]$" + :auxsourcepattern "\\.incf$" + :garbagepattern '("*.o" "*.mod" ".deps/*.P")) + "Fortran 90/95 source code definition.") + +(defvar ede-source-f77 + (ede-sourcecode "ede-source-f77" + :name "Fortran 77" + :sourcepattern "\\.\\([fF]\\|for\\)$" + :auxsourcepattern "\\.incf$" + :garbagepattern '("*.o" ".deps/*.P")) + "Fortran 77 source code definition.") + +(defvar ede-gfortran-compiler + (ede-object-compiler + "ede-f90-compiler-gfortran" + :name "gfortran" + :dependencyvar '("F90_DEPENDENCIES" . "-Wp,-MD,.deps/$(*F).P") + :variables '(("F90" . "gfortran") + ("F90_COMPILE" . + "$(F90) $(DEFS) $(INCLUDES) $(F90FLAGS)")) + :rules (list (ede-makefile-rule + "f90-inference-rule" + :target "%.o" + :dependencies "%.f90" + :rules '("@echo '$(F90_COMPILE) -c $<'; \\" + "$(F90_COMPILE) $(F90_DEPENDENCIES) -o $@ -c $<" + ) + )) + :sourcetype '(ede-source-f90 ede-source-f77) + :objectextention ".o" + :makedepends t + :uselinker t) + "Compiler for Fortran sourcecode.") + +(defvar ede-gfortran-module-compiler + (clone ede-gfortran-compiler + "ede-f90-module-compiler-gfortran" + :name "gfortranmod" + :sourcetype '(ede-source-f90) + :commands '("$(F90_COMPILE) -c $^") + :objectextention ".mod" + :uselinker nil) + "Compiler for Fortran 90/95 modules.") + + +(defvar ede-gfortran-linker + (ede-linker + "ede-gfortran-linker" + :name "gfortran" + :sourcetype '(ede-source-f90 ede-source-f77) + :variables '(("F90_LINK" . + "$(F90) $(CFLAGS) $(LDFLAGS) -L. -o $@") + ) + :commands '("$(F90_LINK) $^") + :objectextention "") + "Linker needed for Fortran programs.") + +;;; Generic Linker +;; +(defvar ede-ld-linker + (ede-linker + "ede-ld-linker" + :name "ld" + :variables '(("LD" . "ld") + ("LD_LINK" . + "$(LD) $(LDFLAGS) -L. -o $@") + ) + :commands '("$(LD_LINK) $^") + :objectextention "") + "Linker needed for c++ programs.") + +;;; The EDE object compiler +;; +(defmethod ede-proj-makefile-insert-variables ((this ede-object-compiler)) + "Insert variables needed by the compiler THIS." + (call-next-method) + (if (eieio-instance-inheritor-slot-boundp this 'dependencyvar) + (with-slots (dependencyvar) this + (insert (car dependencyvar) "=") + (let ((cd (cdr dependencyvar))) + (if (listp cd) + (mapc (lambda (c) (insert " " c)) cd) + (insert cd)) + (insert "\n"))))) + +;;; EDE Object target type methods +;; +(defmethod ede-proj-makefile-sourcevar + ((this ede-proj-target-makefile-objectcode)) + "Return the variable name for THIS's sources." + (require 'ede/pmake) + (concat (ede-pmake-varname this) "_SOURCES")) + +(defmethod ede-proj-makefile-dependency-files + ((this ede-proj-target-makefile-objectcode)) + "Return a list of source files to convert to dependencies. +Argument THIS is the target to get sources from." + (append (oref this source) (oref this auxsource))) + +(defmethod ede-proj-makefile-insert-variables ((this ede-proj-target-makefile-objectcode) + &optional moresource) + "Insert variables needed by target THIS. +Optional argument MORESOURCE is not used." + (let ((ede-proj-objectcode-dodependencies + (oref (ede-target-parent this) automatic-dependencies))) + (call-next-method))) + +(defmethod ede-buffer-header-file((this ede-proj-target-makefile-objectcode) + buffer) + "There are no default header files." + (or (call-next-method) + ;; Ok, nothing obvious. Try looking in ourselves. + (let ((h (oref this auxsource))) + ;; Add more logic here when the problem is better understood. + (car-safe h)))) + +(provide 'ede/proj-obj) + +;;; ede/proj-obj.el ends here diff --git a/lisp/cedet/ede/proj-prog.el b/lisp/cedet/ede/proj-prog.el new file mode 100644 index 00000000000..929004209b2 --- /dev/null +++ b/lisp/cedet/ede/proj-prog.el @@ -0,0 +1,113 @@ +;;; ede-proj-prog.el --- EDE Generic Project program support + +;;; Copyright (C) 1998, 1999, 2000, 2001, 2005, 2008 +;;; 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: +;; +;; Handle building programs from object files in and EDE Project file. + +(require 'ede/pmake) +(require 'ede/proj-obj) + +;;; Code: +(defclass ede-proj-target-makefile-program + (ede-proj-target-makefile-objectcode) + ((ldlibs :initarg :ldlibs + :initform nil + :type list + :custom (repeat (string :tag "Library")) + :documentation + "Libraries, such as \"m\" or \"Xt\" which this program depends on. +The linker flag \"-l\" is automatically prepended. Do not include a \"lib\" +prefix, or a \".so\" suffix. + +Note: Currently only used for Automake projects." + ) + (ldflags :initarg :ldflags + :initform nil + :type list + :custom (repeat (string :tag "Link Flag")) + :documentation + "Additional flags to add when linking this target. +Use ldlibs to add addition libraries. Use this to specify specific +options to the linker. + +Note: Not currently used. This bug needs to be fixed.") + ) + "This target is an executable program.") + +(defmethod ede-proj-makefile-insert-automake-pre-variables + ((this ede-proj-target-makefile-program)) + "Insert bin_PROGRAMS variables needed by target THIS." + (ede-pmake-insert-variable-shared "bin_PROGRAMS" + (insert (ede-name this))) + (call-next-method)) + +(defmethod ede-proj-makefile-insert-automake-post-variables + ((this ede-proj-target-makefile-program)) + "Insert bin_PROGRAMS variables needed by target THIS." + (ede-pmake-insert-variable-shared + (concat (ede-name this) "_LDADD") + (mapc (lambda (c) (insert " -l" c)) (oref this ldlibs))) + ;; For other targets THIS depends on + ;; + ;; NOTE: FIX THIS + ;; + ;;(ede-pmake-insert-variable-shared + ;; (concat (ede-name this) "_DEPENDENCIES") + ;; (mapcar (lambda (d) (insert d)) (oref this FOOOOOOOO))) + (call-next-method)) + +(defmethod ede-proj-makefile-insert-rules ((this ede-proj-target-makefile-program)) + "Insert rules needed by THIS target." + (let ((ede-proj-compiler-object-linkflags + (mapconcat 'identity (oref this ldflags) " "))) + (with-slots (ldlibs) this + (if ldlibs + (setq ede-proj-compiler-object-linkflags + (concat ede-proj-compiler-object-linkflags + " -l" + (mapconcat 'identity ldlibs " -l"))))) + (call-next-method))) + +(defmethod project-debug-target ((obj ede-proj-target-makefile-program)) + "Debug a program target OBJ." + (let ((tb (get-buffer-create " *padt*")) + (dd (if (not (string= (oref obj path) "")) + (oref obj path) + default-directory)) + (cmd nil)) + (unwind-protect + (progn + (set-buffer tb) + (setq default-directory dd) + (setq cmd (read-from-minibuffer + "Run (like this): " + (concat (symbol-name ede-debug-program-function) + " " (ede-target-name obj)))) + (funcall ede-debug-program-function cmd)) + (kill-buffer tb)))) + + +(provide 'ede/proj-prog) + +;;; ede/proj-prog.el ends here diff --git a/lisp/cedet/ede/proj-scheme.el b/lisp/cedet/ede/proj-scheme.el new file mode 100644 index 00000000000..09d79fc22c0 --- /dev/null +++ b/lisp/cedet/ede/proj-scheme.el @@ -0,0 +1,49 @@ +;;; ede/proj-scheme.el --- EDE Generic Project scheme (guile) support + +;;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Keywords: project, make, scheme + +;; 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: +;; +;; Handle scheme (Guile) in and EDE Project file. +;; This is a specialized do nothing class. + +(require 'ede/proj) +(require 'ede/autoconf-edit) + +;;; Code: +(defclass ede-proj-target-scheme (ede-proj-target) + ((menu :initform nil) + (keybindings :initform nil) + (interpreter :initarg :interpreter + :initform "guile" + :type string + :custom string + :documentation "The preferred interpreter for this code.") + ) + "This target consists of scheme files.") + +(defmethod ede-proj-tweak-autoconf ((this ede-proj-target-scheme)) + "Tweak the configure file (current buffer) to accomodate THIS." + (autoconf-insert-new-macro "AM_INIT_GUILE_MODULE")) + +(provide 'ede/proj-scheme) + +;;; ede/proj-scheme.el ends here diff --git a/lisp/cedet/ede/proj-shared.el b/lisp/cedet/ede/proj-shared.el new file mode 100644 index 00000000000..45051032d9c --- /dev/null +++ b/lisp/cedet/ede/proj-shared.el @@ -0,0 +1,164 @@ +;;; ede-proj-shared.el --- EDE Generic Project shared library support + +;;; Copyright (C) 1998, 1999, 2000, 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: +;; +;; Handle shared object libraries in and EDE Project file. +;; Tries to deal with libtool and non-libtool situations. + +(require 'ede/pmake) +(require 'ede/proj-prog) + +;;; THIS NEEDS WORK. SEE ede-proj-obj. + +;;; Code: +(defclass ede-proj-target-makefile-shared-object + (ede-proj-target-makefile-program) + ((availablecompilers :initform (ede-gcc-shared-compiler + ede-gcc-libtool-shared-compiler + ede-g++-shared-compiler + ede-g++-libtool-shared-compiler + )) + (ldflags :custom (repeat (string :tag "Libtool flag")) + :documentation + "Additional flags to add when linking this shared library. +Use ldlibs to add addition libraries.") + ) + "This target generates a shared library.") + +(defvar ede-gcc-shared-compiler + (clone ede-gcc-compiler + "ede-c-shared-compiler" + :name "gcc -shared" + :variables '(("CC_SHARED" . "gcc") + ("C_SHARED_COMPILE" . + "$(CC_SHARED) -shared $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)")) +; :linkvariables '(("C_SHARED_LINK" . +; "$(CC_SHARED) -shared $(CFLAGS) $(LDFLAGS) -L. -o $@ $^") +; ) +; :commands '("$(C_SHARED_LINK) %s") + ;; @TODO - addative modification of autoconf. + :autoconf '("AC_PROG_LIBTOOL") + ) + "Compiler for C sourcecode.") + +(defvar ede-gcc-libtool-shared-compiler + (clone ede-gcc-shared-compiler + "ede-c-shared-compiler-libtool" + :name "libtool" + :variables '(("LIBTOOL" . "$(SHELL) libtool") + ("LTCOMPILE" . "$(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)") + ("LTLINK" . "$(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(LDFLAGS) -L. -o $@") + ) + :commands '("$(LTLINK) $^" + ) + :autoconf '("AC_PROG_LIBTOOL") + ) + "Compiler for C sourcecode.") + +(defvar ede-g++-shared-compiler + (clone ede-g++-compiler + "ede-c++-shared-compiler" + :name "gcc -shared" + :variables '(("CXX_SHARED" . "g++") + ("CXX_SHARED_COMPILE" . + "$(CXX_SHARED) -shared $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)")) + ;; @TODO - addative modification of autoconf. + :autoconf '("AC_PROG_LIBTOOL") + ) + "Compiler for C sourcecode.") + +(defvar ede-g++-libtool-shared-compiler + (clone ede-g++-shared-compiler + "ede-c++-shared-compiler-libtool" + :name "libtool" + :variables '(("CXX" "g++") + ("LIBTOOL" . "$(SHELL) libtool") + ("LTCOMPILE" . "$(LIBTOOL) --mode=compile $(CXX) $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)") + ("LTLINK" . "$(LIBTOOL) --mode=link $(CXX) $(CFLAGS) $(LDFLAGS) -L. -o $@") + ) + :commands '("$(LTLINK) $^" + ) + :autoconf '("AC_PROG_LIBTOOL") + ) + "Compiler for C sourcecode.") + +;;; @TODO - C++ versions of the above. + +(when nil + + + (insert;; These C to O rules create dependencies + "%.o: %.c\n" + "\t@echo '$(COMPILE) -c $<'; \\\n" + "\t$(COMPILE)" + (if (oref this automatic-dependencies) + " -Wp,-MD,.deps/$(*F).P" + "") + " -c $<\n\n") + (if have-libtool + (insert;; These C to shared o rules create pic code. + "%.lo: %.c\n" + "\t@echo '$(LTCOMPILE) -c $<'; \\\n" + "\t$(LTCOMPILE) -Wp,-MD,.deps/$(*F).p -c $<\n" + "\t@-sed -e 's/^\([^:]*\)\.o:/\1.lo \1.o:/' \\\n" + "\t < .deps/$(*F).p > .deps/$(*F).P\n" + "\t@-rm -f .deps/$(*F).p\n\n")) + ) + +(defmethod ede-proj-configure-add-missing + ((this ede-proj-target-makefile-shared-object)) + "Query if any files needed by THIS provided by automake are missing. +Results in --add-missing being passed to automake." + (not (and (ede-expand-filename (ede-toplevel) "ltconfig") + (ede-expand-filename (ede-toplevel) "ltmain.sh")))) + +(defmethod ede-proj-makefile-insert-automake-pre-variables + ((this ede-proj-target-makefile-shared-object)) + "Insert bin_PROGRAMS variables needed by target THIS. +We aren't acutally inserting SOURCE details, but this is used by the +Makefile.am generator, so use it to add this important bin program." + (ede-pmake-insert-variable-shared "lib_LTLIBRARIES" + (insert (concat "lib" (ede-name this) ".la")))) + +(defmethod ede-proj-makefile-insert-automake-post-variables + ((this ede-proj-target-makefile-shared-object)) + "Insert bin_PROGRAMS variables needed by target THIS. +We need to override -program which has an LDADD element." + nil) + +(defmethod ede-proj-makefile-target-name ((this ede-proj-target-makefile-shared-object)) + "Return the name of the main target for THIS target." + ;; We need some platform gunk to make the .so change to .sl, or .a, + ;; depending on the platform we are going to compile against. + (concat "lib" (ede-name this) ".so")) + +(defmethod ede-proj-makefile-sourcevar ((this ede-proj-target-makefile-shared-object)) + "Return the variable name for THIS's sources." + (if (eq (oref (ede-target-parent this) makefile-type) 'Makefile.am) + (concat "lib" (oref this name) "_la_SOURCES") + (call-next-method))) + + +(provide 'ede/proj-shared) + +;;; ede/proj-shared.el ends here diff --git a/lisp/cedet/ede/proj.el b/lisp/cedet/ede/proj.el new file mode 100644 index 00000000000..d74050e758f --- /dev/null +++ b/lisp/cedet/ede/proj.el @@ -0,0 +1,675 @@ +;;; ede-proj.el --- EDE Generic Project file driver + +;;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2007, 2008, 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: +;; +;; EDE defines a method for managing a project. EDE-PROJ aims to be a +;; generic project file format based on the EIEIO object stream +;; methods. Changes in the project structure will require Makefile +;; rebuild. The targets provided in ede-proj can be augmented with +;; additional target types inherited directly from `ede-proj-target'. + +;; (eval-and-compile '(require 'ede)) +(require 'ede/proj-comp) +(require 'ede/make) + +(declare-function ede-proj-makefile-create "ede/pmake") +(declare-function ede-proj-configure-synchronize "ede/pconf") + +(autoload 'ede-proj-target-aux "ede/proj-aux" + "Target class for a group of lisp files." nil nil) +(autoload 'ede-proj-target-elisp "ede/proj-elisp" + "Target class for a group of lisp files." nil nil) +(autoload 'ede-proj-target-elisp-autoloads "ede/proj-elisp" + "Target class for generating autoload files." nil nil) +(autoload 'ede-proj-target-scheme "ede/proj-scheme" + "Target class for a group of lisp files." nil nil) +(autoload 'ede-proj-target-makefile-miscelaneous "ede/proj-misc" + "Target class for a group of miscelaneous w/ a special makefile." nil nil) +(autoload 'ede-proj-target-makefile-program "ede/proj-prog" + "Target class for building a program." nil nil) +(autoload 'ede-proj-target-makefile-archive "ede/proj-archive" + "Target class for building an archive of object code." nil nil) +(autoload 'ede-proj-target-makefile-shared-object "ede/proj-shared" + "Target class for building a shared object." nil nil) +(autoload 'ede-proj-target-makefile-info "ede/proj-info" + "Target class for info files." nil nil) + +;;; Class Definitions: +(defclass ede-proj-target (ede-target) + ((auxsource :initarg :auxsource + :initform nil + :type list + :custom (repeat (string :tag "File")) + :label "Auxiliary Source Files" + :group (default source) + :documentation "Auxilliary source files included in this target. +Each of these is considered equivalent to a source file, but it is not +distributed, and each should have a corresponding rule to build it.") + (dirty :initform nil + :type boolean + :documentation "Non-nil when generated files needs updating.") + (compiler :initarg :compiler + :initform nil + :type (or null symbol) + :custom (choice (const :tag "None" nil) + :slotofchoices availablecompilers) + :label "Compiler for building sources" + :group make + :documentation + "The compiler to be used to compile this object. +This should be a symbol, which contains the object defining the compiler. +This enables save/restore to do so by name, permitting the sharing +of these compiler resources, and global customization thereof.") + (linker :initarg :linker + :initform nil + :type (or null symbol) + :custom (choice (const :tag "None" nil) + :slotofchoices availablelinkers) + :label "Linker for combining intermediate object files." + :group make + :documentation + "The linker to be used to link compiled sources for this object. +This should be a symbol, which contains the object defining the linker. +This enables save/restore to do so by name, permitting the sharing +of these linker resources, and global customization thereof.") + ;; Class allocated slots + (phony :allocation :class + :initform nil + :type boolean + :documentation + "A phony target is one where the build target does not relate to a file. +Such targets are always built, but make knows how to deal with them..") + (availablecompilers :allocation :class + :initform nil + :type (or null list) + :documentation + "A list of `ede-compiler' objects. +These are the compilers the user can choose from when setting the +`compiler' slot.") + (availablelinkers :allocation :class + :initform nil + :type (or null list) + :documentation + "A list of `ede-linker' objects. +These are the linkers the user can choose from when setting the +`linker' slot.") + ) + "Abstract class for ede-proj targets.") + +(defclass ede-proj-target-makefile (ede-proj-target) + ((makefile :initarg :makefile + :initform "Makefile" + :type string + :custom string + :label "Parent Makefile" + :group make + :documentation "File name of generated Makefile.") + (partofall :initarg :partofall + :initform t + :type boolean + :custom boolean + :label "Part of `all:' target" + :group make + :documentation + "Non nil means the rule created is part of the all target. +Setting this to nil creates the rule to build this item, but does not +include it in the ALL`all:' rule.") + (configuration-variables + :initarg :configuration-variables + :initform nil + :type list + :custom (repeat (cons (string :tag "Configuration") + (repeat + (cons (string :tag "Name") + (string :tag "Value"))))) + :label "Environment Variables for configurations" + :group make + :documentation "Makefile variables appended to use in different configurations. +These variables are used in the makefile when a configuration becomes active. +Target variables are always renamed such as foo_CFLAGS, then included into +commands where the variable would usually appear.") + (rules :initarg :rules + :initform nil + :type list + :custom (repeat (object :objecttype ede-makefile-rule)) + :label "Additional Rules" + :group (make) + :documentation + "Arbitrary rules and dependencies needed to make this target. +It is safe to leave this blank.") + ) + "Abstract class for Makefile based targets.") + +(defvar ede-proj-target-alist + '(("program" . ede-proj-target-makefile-program) + ("archive" . ede-proj-target-makefile-archive) + ("sharedobject" . ede-proj-target-makefile-shared-object) + ("emacs lisp" . ede-proj-target-elisp) + ("emacs lisp autoloads" . ede-proj-target-elisp-autoloads) + ("info" . ede-proj-target-makefile-info) + ("auxiliary" . ede-proj-target-aux) + ("scheme" . ede-proj-target-scheme) + ("miscellaneous" . ede-proj-target-makefile-miscelaneous) + ) + "Alist of names to class types for available project target classes.") + +(defun ede-proj-register-target (name class) + "Register a new target class with NAME and class symbol CLASS. +This enables the creation of your target type." + (let ((a (assoc name ede-proj-target-alist))) + (if a + (setcdr a class) + (setq ede-proj-target-alist + (cons (cons name class) ede-proj-target-alist))))) + +(defclass ede-proj-project (ede-project) + ((makefile-type :initarg :makefile-type + :initform Makefile + :type symbol + :custom (choice (const Makefile) + ;(const Makefile.in) + (const Makefile.am) + ;(const cook) + ) + :documentation "The type of Makefile to generate. +Can be one of 'Makefile, 'Makefile.in, or 'Makefile.am. +If this value is NOT 'Makefile, then that overrides the :makefile slot +in targets.") + (variables :initarg :variables + :initform nil + :type list + :custom (repeat (cons (string :tag "Name") + (string :tag "Value"))) + :group (settings) + :documentation "Variables to set in this Makefile.") + (configuration-variables + :initarg :configuration-variables + :initform ("debug" (("DEBUG" . "1"))) + :type list + :custom (repeat (cons (string :tag "Configuration") + (repeat + (cons (string :tag "Name") + (string :tag "Value"))))) + :group (settings) + :documentation "Makefile variables to use in different configurations. +These variables are used in the makefile when a configuration becomes active.") + (inference-rules :initarg :inference-rules + :initform nil + :custom (repeat + (object :objecttype ede-makefile-rule)) + :documentation "Inference rules to add to the makefile.") + (include-file :initarg :include-file + :initform nil + :custom (repeat + (string :tag "Include File")) + :documentation "Additional files to include. +These files can contain additional rules, variables, and customizations.") + (automatic-dependencies + :initarg :automatic-dependencies + :initform t + :type boolean + :custom boolean + :group (default settings) + :documentation + "Non-nil to do implement automatic dependencies in the Makefile.") + (menu :initform + ( + [ "Regenerate Makefiles" ede-proj-regenerate t ] + [ "Upload Distribution" ede-upload-distribution t ] + ) + ) + (metasubproject + :initarg :metasubproject + :initform nil + :type boolean + :custom boolean + :group (default settings) + :documentation + "Non-nil if this is a metasubproject. +Usually, a subproject is determined by a parent project. If multiple top level +projects are grouped into a large project not maintained by EDE, then you need +to set this to non-nil. The only effect is that the `dist' rule will then avoid +making a tar file.") + ) + "The EDE-PROJ project definition class.") + +;;; Code: +(defun ede-proj-load (project &optional rootproj) + "Load a project file from PROJECT directory. +If optional ROOTPROJ is provided then ROOTPROJ is the root project +for the tree being read in. If ROOTPROJ is nil, then assume that +the PROJECT being read in is the root project." + (save-excursion + (let ((ret nil) + (subdirs (directory-files project nil "[^.].*" nil))) + (set-buffer (get-buffer-create " *tmp proj read*")) + (unwind-protect + (progn + (insert-file-contents (concat project "Project.ede") + nil nil nil t) + (goto-char (point-min)) + (setq ret (read (current-buffer))) + (if (not (eq (car ret) 'ede-proj-project)) + (error "Corrupt project file")) + (setq ret (eval ret)) + (oset ret file (concat project "Project.ede")) + (oset ret directory project) + (oset ret rootproject rootproj) + ) + (kill-buffer " *tmp proj read*")) + (while subdirs + (let ((sd (file-name-as-directory + (expand-file-name (car subdirs) project)))) + (if (and (file-directory-p sd) + (ede-directory-project-p sd)) + (oset ret subproj + (cons (ede-proj-load sd (or rootproj ret)) + (oref ret subproj)))) + (setq subdirs (cdr subdirs)))) + ret))) + +(defun ede-proj-save (&optional project) + "Write out object PROJECT into its file." + (save-excursion + (if (not project) (setq project (ede-current-project))) + (let ((b (set-buffer (get-buffer-create " *tmp proj write*"))) + (cfn (oref project file)) + (cdir (oref project directory))) + (unwind-protect + (save-excursion + (erase-buffer) + (let ((standard-output (current-buffer))) + (oset project file (file-name-nondirectory cfn)) + (slot-makeunbound project :directory) + (object-write project ";; EDE project file.")) + (write-file cfn nil) + ) + ;; Restore the :file on exit. + (oset project file cfn) + (oset project directory cdir) + (kill-buffer b))))) + +(defmethod ede-commit-local-variables ((proj ede-proj-project)) + "Commit change to local variables in PROJ." + (ede-proj-save proj)) + +(defmethod eieio-done-customizing ((proj ede-proj-project)) + "Call this when a user finishes customizing this object. +Argument PROJ is the project to save." + (call-next-method) + (ede-proj-save proj)) + +(defmethod eieio-done-customizing ((target ede-proj-target)) + "Call this when a user finishes customizing this object. +Argument TARGET is the project we are completing customization on." + (call-next-method) + (ede-proj-save (ede-current-project))) + +(defmethod ede-commit-project ((proj ede-proj-project)) + "Commit any change to PROJ to its file." + (ede-proj-save proj)) + +(defmethod ede-buffer-mine ((this ede-proj-project) buffer) + "Return t if object THIS lays claim to the file in BUFFER." + (let ((f (ede-convert-path this (buffer-file-name buffer)))) + (or (string= (file-name-nondirectory (oref this file)) f) + (string= (ede-proj-dist-makefile this) f) + (string-match "Makefile\\(\\.\\(in\\|am\\)\\)?$" f) + (string-match "config\\(ure\\.in\\|\\.stutus\\)?$" f) + ))) + +(defmethod ede-buffer-mine ((this ede-proj-target) buffer) + "Return t if object THIS lays claim to the file in BUFFER." + (or (call-next-method) + (ede-target-buffer-in-sourcelist this buffer (oref this auxsource)))) + + +;;; EDE command functions +;; +(defvar ede-proj-target-history nil + "History when querying for a target type.") + +(defmethod project-new-target ((this ede-proj-project) + &optional name type autoadd) + "Create a new target in THIS based on the current buffer." + (let* ((name (or name (read-string "Name: " ""))) + (type (or type + (completing-read "Type: " ede-proj-target-alist + nil t nil '(ede-proj-target-history . 1)))) + (ot nil) + (src (if (and (buffer-file-name) + (if (and autoadd (stringp autoadd)) + (string= autoadd "y") + (y-or-n-p (format "Add %s to %s? " (buffer-name) name)))) + (buffer-file-name))) + (fcn (cdr (assoc type ede-proj-target-alist))) + ) + + (when (not fcn) + (error "Unknown target type %s for EDE Project." type)) + + (setq ot (funcall fcn name :name name + :path (ede-convert-path this default-directory) + :source (if src + (list (file-name-nondirectory src)) + nil))) + ;; If we added it, set the local buffer's object. + (if src (progn + (setq ede-object ot) + (ede-apply-object-keymap))) + ;; Add it to the project object + ;;(oset this targets (cons ot (oref this targets))) + ;; New form: Add to the end using fancy eieio function. + ;; @todone - Some targets probably want to be in the front. + ;; How to do that? + ;; @ans - See elisp autoloads for answer + (object-add-to-list this 'targets ot t) + ;; And save + (ede-proj-save this))) + +(defmethod project-new-target-custom ((this ede-proj-project)) + "Create a new target in THIS for custom." + (let* ((name (read-string "Name: " "")) + (type (completing-read "Type: " ede-proj-target-alist + nil t nil '(ede-proj-target-history . 1)))) + (funcall (cdr (assoc type ede-proj-target-alist)) name :name name + :path (ede-convert-path this default-directory) + :source nil))) + +(defmethod project-delete-target ((this ede-proj-target)) + "Delete the current target THIS from it's parent project." + (let ((p (ede-current-project)) + (ts (oref this source))) + ;; Loop across all sources. If it exists in a buffer, + ;; clear it's object. + (while ts + (let* ((default-directory (oref this path)) + (b (get-file-buffer (car ts)))) + (if b + (save-excursion + (set-buffer b) + (if (eq ede-object this) + (progn + (setq ede-object nil) + (ede-apply-object-keymap)))))) + (setq ts (cdr ts))) + ;; Remove THIS from it's parent. + ;; The two vectors should be pointer equivalent. + (oset p targets (delq this (oref p targets))) + (ede-proj-save (ede-current-project)))) + +(defmethod project-add-file ((this ede-proj-target) file) + "Add to target THIS the current buffer represented as FILE." + (let ((file (ede-convert-path this file)) + (src (ede-target-sourcecode this))) + (while (and src (not (ede-want-file-p (car src) file))) + (setq src (cdr src))) + (when src + (setq src (car src)) + (cond ((ede-want-file-source-p this file) + (object-add-to-list this 'source file t)) + ((ede-want-file-auxiliary-p this file) + (object-add-to-list this 'auxsource file t)) + (t (error "`project-add-file(ede-target)' source mismatch error"))) + (ede-proj-save)))) + +(defmethod project-remove-file ((target ede-proj-target) file) + "For TARGET, remove FILE. +FILE must be massaged by `ede-convert-path'." + ;; Speedy delete should be safe. + (object-remove-from-list target 'source (ede-convert-path target file)) + (object-remove-from-list target 'auxsource (ede-convert-path target file)) + (ede-proj-save)) + +(defmethod project-update-version ((this ede-proj-project)) + "The :version of project THIS has changed." + (ede-proj-save)) + +(defmethod project-make-dist ((this ede-proj-project)) + "Build a distribution for the project based on THIS target." + ;; I'm a lazy bum, so I'll make a makefile for doing this sort + ;; of thing, and rely only on that small section of code. + (let ((pm (ede-proj-dist-makefile this)) + (df (project-dist-files this))) + (if (and (file-exists-p (car df)) + (not (y-or-n-p "Dist file already exists. Rebuild? "))) + (error "Try `ede-update-version' before making a distribution")) + (ede-proj-setup-buildenvironment this) + (if (string= pm "Makefile.am") (setq pm "Makefile")) + (compile (concat ede-make-command " -f " pm " dist")) + )) + +(defmethod project-dist-files ((this ede-proj-project)) + "Return a list of files that constitutes a distribution of THIS project." + (list + ;; Note to self, keep this first for the above fn to check against. + (concat (oref this name) "-" (oref this version) ".tar.gz") + )) + +(defmethod project-compile-project ((proj ede-proj-project) &optional command) + "Compile the entire current project PROJ. +Argument COMMAND is the command to use when compiling." + (let ((pm (ede-proj-dist-makefile proj)) + (default-directory (file-name-directory (oref proj file)))) + (ede-proj-setup-buildenvironment proj) + (if (string= pm "Makefile.am") (setq pm "Makefile")) + (compile (concat ede-make-command" -f " pm " all")))) + +;;; Target type specific compilations/debug +;; +(defmethod project-compile-target ((obj ede-proj-target) &optional command) + "Compile the current target OBJ. +Argument COMMAND is the command to use for compiling the target." + (project-compile-project (ede-current-project) command)) + +(defmethod project-compile-target ((obj ede-proj-target-makefile) + &optional command) + "Compile the current target program OBJ. +Optional argument COMMAND is the s the alternate command to use." + (ede-proj-setup-buildenvironment (ede-current-project)) + (compile (concat ede-make-command " -f " (oref obj makefile) " " + (ede-proj-makefile-target-name obj)))) + +(defmethod project-debug-target ((obj ede-proj-target)) + "Run the current project target OBJ in a debugger." + (error "Debug-target not supported by %s" (object-name obj))) + +(defmethod ede-proj-makefile-target-name ((this ede-proj-target)) + "Return the name of the main target for THIS target." + (ede-name this)) + +;;; Compiler and source code generators +;; +(defmethod ede-want-file-auxiliary-p ((this ede-target) file) + "Return non-nil if THIS target wants FILE." + ;; By default, all targets reference the source object, and let it decide. + (let ((src (ede-target-sourcecode this))) + (while (and src (not (ede-want-file-auxiliary-p (car src) file))) + (setq src (cdr src))) + src)) + +(defmethod ede-proj-compilers ((obj ede-proj-target)) + "List of compilers being used by OBJ. +If the `compiler' slot is empty, concoct one on a first match found +basis for any given type from the `availablecompilers' slot. +Otherwise, return the `compiler' slot. +Converts all symbols into the objects to be used." + (when (slot-exists-p obj 'compiler) + (let ((comp (oref obj compiler))) + (if comp + ;; Now that we have a pre-set compilers to use, convert tye symbols + ;; into objects for ease of use + (if (listp comp) + (setq comp (mapcar 'symbol-value comp)) + (setq comp (list (symbol-value comp)))) + (let* ((acomp (oref obj availablecompilers)) + (avail (mapcar 'symbol-value acomp)) + (st (oref obj sourcetype)) + (sources (oref obj source))) + ;; COMP is not specified, so generate a list from the available + ;; compilers list. + (while st + (if (ede-want-any-source-files-p (symbol-value (car st)) sources) + (let ((c (ede-proj-find-compiler avail (car st)))) + (if c (setq comp (cons c comp))))) + (setq st (cdr st))))) + ;; Return the disovered compilers + comp))) + +(defmethod ede-proj-linkers ((obj ede-proj-target)) + "List of linkers being used by OBJ. +If the `linker' slot is empty, concoct one on a first match found +basis for any given type from the `availablelinkers' slot. +Otherwise, return the `linker' slot. +Converts all symbols into the objects to be used." + (when (slot-exists-p obj 'linker) + (let ((link (oref obj linker))) + (if link + ;; Now that we have a pre-set linkers to use, convert type symbols + ;; into objects for ease of use + (if (symbolp link) + (setq link (list (symbol-value link))) + (error ":linker is not a symbol. Howd you do that?")) + (let* ((alink (oref obj availablelinkers)) + (avail (mapcar 'symbol-value alink)) + (st (oref obj sourcetype)) + (sources (oref obj source))) + ;; LINKER is not specified, so generate a list from the available + ;; compilers list. + (while st + (if (ede-want-any-source-files-p (symbol-value (car st)) sources) + (let ((c (ede-proj-find-linker avail (car st)))) + (if c (setq link (cons c link))))) + (setq st (cdr st))) + (unless link + ;; No linker stands out! Loop over our linkers and pull out + ;; the first that has no source type requirement. + (while (and avail (not (eieio-instance-inheritor-slot-boundp (car avail) 'sourcetype))) + (setq avail (cdr avail))) + (setq link (cdr avail))))) + ;; Return the disovered linkers + link))) + + +;;; Target type specific autogenerating gobbldegook. +;; + +(defun ede-proj-makefile-type (&optional proj) + "Makefile type of the current project PROJ." + (oref (or proj (ede-current-project)) makefile-type)) + +(defun ede-proj-automake-p (&optional proj) + "Return non-nil if the current project PROJ is automake mode." + (eq (ede-proj-makefile-type proj) 'Makefile.am)) + +(defun ede-proj-autoconf-p (&optional proj) + "Return non-nil if the current project PROJ is automake mode." + (eq (ede-proj-makefile-type proj) 'Makefile.in)) + +(defun ede-proj-make-p (&optional proj) + "Return non-nil if the current project PROJ is automake mode." + (eq (ede-proj-makefile-type proj) 'Makefile)) + +(defmethod ede-proj-dist-makefile ((this ede-proj-project)) + "Return the name of the Makefile with the DIST target in it for THIS." + (cond ((eq (oref this makefile-type) 'Makefile.am) + (concat (file-name-directory (oref this file)) + "Makefile.am")) + ((eq (oref this makefile-type) 'Makefile.in) + (concat (file-name-directory (oref this file)) + "Makefile.in")) + ((object-assoc "Makefile" 'makefile (oref this targets)) + (concat (file-name-directory (oref this file)) + "Makefile")) + (t + (let ((targets (oref this targets))) + (while (and targets + (not (obj-of-class-p + (car targets) + 'ede-proj-target-makefile))) + (setq targets (cdr targets))) + (if targets (oref (car targets) makefile) + (concat (file-name-directory (oref this file)) + "Makefile")))))) + +(defun ede-proj-regenerate () + "Regenerate Makefiles for and edeproject project." + (interactive) + (ede-proj-setup-buildenvironment (ede-current-project) t)) + +(defmethod ede-proj-makefile-create-maybe ((this ede-proj-project) mfilename) + "Create a Makefile for all Makefile targets in THIS if needed. +MFILENAME is the makefile to generate." + ;; For now, pass through until dirty is implemented. + (require 'ede/pmake) + (if (or (not (file-exists-p mfilename)) + (file-newer-than-file-p (oref this file) mfilename)) + (ede-proj-makefile-create this mfilename))) + +(defmethod ede-proj-setup-buildenvironment ((this ede-proj-project) + &optional force) + "Setup the build environment for project THIS. +Handles the Makefile, or a Makefile.am configure.in combination. +Optional argument FORCE will force items to be regenerated." + (if (not force) + (ede-proj-makefile-create-maybe this (ede-proj-dist-makefile this)) + (require 'ede/pmake) + (ede-proj-makefile-create this (ede-proj-dist-makefile this))) + ;; Rebuild all subprojects + (ede-map-subprojects + this (lambda (sproj) (ede-proj-setup-buildenvironment sproj force))) + ;; Autoconf projects need to do other kinds of initializations. + (when (and (ede-proj-automake-p this) + (eq this (ede-toplevel this))) + (require 'ede/pconf) + ;; If the user wants to force this, do it some other way? + (ede-proj-configure-synchronize this) + ;; Now run automake to fill in the blanks, autoconf, and other + ;; auto thingies so that we can just say "make" when done. + ) + ) + + +;;; Lower level overloads +;; +(defmethod project-rescan ((this ede-proj-project)) + "Rescan the EDE proj project THIS." + (let ((root (or (ede-project-root this) this)) + ) + (setq ede-projects (delq root ede-projects)) + (ede-proj-load (ede-project-root-directory root)) + )) + +(defmethod project-rescan ((this ede-proj-target) readstream) + "Rescan target THIS from the read list READSTREAM." + (setq readstream (cdr (cdr readstream))) ;; constructor/name + (while readstream + (let ((tag (car readstream)) + (val (car (cdr readstream)))) + (eieio-oset this tag val)) + (setq readstream (cdr (cdr readstream))))) + +(provide 'ede/proj) + +;;; ede/proj.el ends here diff --git a/lisp/cedet/ede/project-am.el b/lisp/cedet/ede/project-am.el new file mode 100644 index 00000000000..976f72e6c7d --- /dev/null +++ b/lisp/cedet/ede/project-am.el @@ -0,0 +1,994 @@ +;;; project-am.el --- A project management scheme based on automake files. + +;;; Copyright (C) 1998, 1999, 2000, 2003, 2005, 2007, 2008, 2009 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Version: 0.0.3 +;; 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: +;; +;; The GNU Automake tool is the first step towards having a really +;; good project management system. It provides a simple and concise +;; look at what is actually in a project, and records it in a simple +;; fashion. +;; +;; project-am uses the structure defined in all good GNU projects with +;; the Automake file as it's base template, and then maintains that +;; information during edits, automatically updating the automake file +;; where appropriate. + + +;; (eval-and-compile +;; ;; Compatibility for makefile mode. +;; (condition-case nil +;; (require 'makefile "make-mode") +;; (error (require 'make-mode "make-mode"))) + +;; ;; Requiring the .el files prevents incomplete builds. +;; (require 'eieio "eieio.el") +;; (require 'ede "ede.el")) + +(require 'make-mode) +(require 'ede) +(require 'ede/make) +(require 'ede/makefile-edit) + +(declare-function autoconf-parameters-for-macro "ede/autoconf-edit") +(eval-when-compile (require 'compile)) + +;;; Code: +(defgroup project-am nil + "File and tag browser frame." + :group 'tools + :group 'ede + ) + +(defcustom project-am-compile-project-command nil + "*Default command used to compile a project." + :group 'project-am + :type 'string) + +(defcustom project-am-compile-target-command (concat ede-make-command " -k %s") + "*Default command used to compile a project." + :group 'project-am + :type 'string) + +(defcustom project-am-debug-target-function 'gdb + "*Default Emacs command used to debug a target." + :group 'project-am + :type 'sexp) ; make this be a list some day + +(defconst project-am-type-alist + '(("bin" project-am-program "bin_PROGRAMS" t) + ("sbin" project-am-program "sbin_PROGRAMS" t) + ("noinstbin" project-am-program "noinst_PROGRAMS" t) + ("checkbin" project-am-program "check_PROGRAMS" t) + ("lib" project-am-lib "lib_LIBS" t) + ("libraries" project-am-lib "lib_LIBRARIES" t) + ("librariesnoinst" project-am-lib "noinst_LIBRARIES" t) + ("pkglibraries" project-am-lib "pkglib_LIBRARIES" t) + ("checklibs" project-am-lib "check_LIBRARIES" t) + ("ltlibraries" project-am-lib "lib_LTLIBRARIES" t) + ("ltlibrariesnoinst" project-am-lib "noinst_LTLIBRARIES" t) + ("pkgltlibraries" project-am-lib "pkglib_LTLIBRARIES" t) + ("checkltlibs" project-am-lib "check_LTLIBRARIES" t) + ("headernoinst" project-am-header-noinst "noinst_HEADERS") + ("headerinst" project-am-header-inst "include_HEADERS") + ("headerpkg" project-am-header-pkg "pkginclude_HEADERS") + ("headerpkg" project-am-header-chk "check_HEADERS") + ("texinfo" project-am-texinfo "info_TEXINFOS" t) + ("man" project-am-man "man_MANS") + ("lisp" project-am-lisp "lisp_LISP") + ;; for other global files track EXTRA_ + ("extrabin" project-am-program "EXTRA_PROGRAMS" t) + ("builtsrcs" project-am-built-src "BUILT_SOURCES") + ("extradist" project-am-extra-dist "EXTRA_DIST") + ;; Custom libraries targets? + ;; ("ltlibcustom" project-am-lib ".*?_LTLIBRARIES" t) + ) + "Alist of type names and the type of object to create for them. +Each entry is of th form: + (EMACSNAME CLASS AUToMAKEVAR INDIRECT) +where EMACSNAME is a name for Emacs to use. +CLASS is the EDE target class to represent the target. +AUTOMAKEVAR is the Automake variable to identify. This cannot be a + regular expression. +INDIRECT is optional. If it is non-nil, then the variable in +question lists other variables that need to be looked up.") + +(defclass project-am-target (ede-target) + nil + "Base target class for everything in project-am.") + +(defclass project-am-objectcode (project-am-target) + ((source :initarg :source :documentation "List of source files.")) + "A target which creates object code, like a C program or library.") + +(defclass project-am-program (project-am-objectcode) + ((ldadd :initarg :ldadd :documentation "Additional LD args." + :initform nil)) + "A top level program to build") + +(defclass project-am-header (project-am-target) + () + "A group of misc source files, such as headers.") + +(defclass project-am-header-noinst (project-am-header) + () + "A group of header files that are not installed.") + +(defclass project-am-header-inst (project-am-header) + () + "A group of header files that are not installed.") + +(defclass project-am-header-pkg (project-am-header) + () + "A group of header files that are not installed.") + +(defclass project-am-header-chk (project-am-header) + () + "A group of header files that are not installed.") + +(defclass project-am-lib (project-am-objectcode) + nil + "A top level library to build") + +(defclass project-am-lisp (project-am-target) + () + "A group of Emacs Lisp programs to byte compile.") + +(defclass project-am-texinfo (project-am-target) + ((include :initarg :include + :initform nil + :documentation "Additional texinfo included in this one.")) + "A top level texinfo file to build.") + +(defclass project-am-man (project-am-target) + nil + "A top level man file to build.") + +;; For generic files tracker like EXTRA_DIST +(defclass project-am-built-src (project-am-target) + () + "A group of Emacs Lisp programs to byte compile.") + +(defclass project-am-extra-dist (project-am-target) + () + "A group of Emacs Lisp programs to byte compile.") + +(defclass project-am-makefile (ede-project) + ((targets :initarg :targets + :initform nil + :documentation "Top level targets in this makefile.") + (configureoutputfiles + :initform nil + :documentation + "List of files output from configure system.") + ) + "Encode one makefile.") + +;;; Code: +(defmethod project-add-file ((ot project-am-target)) + "Add the current buffer into a project. +OT is the object target. DIR is the directory to start in." + (let* ((target (if ede-object (error "Already assocated w/ a target") + (let ((amf (project-am-load default-directory))) + (if (not amf) (error "No project file")) + (completing-read "Target: " + (object-assoc-list 'name + (oref amf targets)) + nil t)))) + ;; The input target might be new. See if we can find it. + (amf (ede-load-project-file (oref ot path))) + (ot (object-assoc target 'name (oref amf targets))) + (ofn (file-name-nondirectory (buffer-file-name)))) + (if (not ot) + (setq ot + (project-new-target + target (project-am-preferred-target-type (buffer-file-name))))) + (ede-with-projectfile ot + (makefile-move-to-macro (project-am-macro ot)) + (ede-maybe-checkout) + (makefile-end-of-command) + (insert " " ofn) + (makefile-fill-paragraph nil) + (project-rescan ot) + (save-buffer)) + (setq ede-object ot))) + +(defmethod project-remove-file ((ot project-am-target) fnnd) + "Remove the current buffer from any project targets." + (ede-with-projectfile ot + (makefile-move-to-macro (project-am-macro ot)) + (if (and buffer-read-only vc-mode + (y-or-n-p "Checkout Makefile.am from VC? ")) + (vc-toggle-read-only t)) + (ede-maybe-checkout) + (makefile-navigate-macro (concat " *" (regexp-quote (ede-name fnnd)))) + (replace-match "" t t nil 0) + (makefile-fill-paragraph nil) + (project-rescan ot) + (save-buffer)) + (setq ede-object nil)) + +(defmethod project-edit-file-target ((obj project-am-target)) + "Edit the target associated w/ this file." + (find-file (concat (oref obj path) "Makefile.am")) + (goto-char (point-min)) + (makefile-move-to-macro (project-am-macro obj)) + (if (= (point-min) (point)) + (re-search-forward (ede-target-name obj)))) + +(defmethod project-new-target ((proj project-am-makefile) + &optional name type) + "Create a new target named NAME. +Argument TYPE is the type of target to insert. This is a string +matching something in `project-am-type-alist' or type class symbol. +Despite the fact that this is a method, it depends on the current +buffer being in order to provide a smart default target type." + (let* ((name (or name (read-string "Name: " ""))) + (type (or type + (completing-read "Type: " + project-am-type-alist + nil t + (cond ((eq major-mode 'texinfo-mode) + "texinfo") + ((eq major-mode 'nroff-mode) + "man") + ((eq major-mode 'emacs-lisp-mode) + "lisp") + (t "bin"))))) + (ntype (assoc type project-am-type-alist)) + (ot nil)) + (setq ot (apply (car (cdr ntype)) name :name name + :path (expand-file-name default-directory) nil)) + (if (not ot) (error "Error creating target object %S" ntype)) + (ede-with-projectfile ot + (goto-char (point-min)) + (ede-maybe-checkout) + (makefile-next-dependency) + (if (= (point) (point-min)) + (goto-char (point-max)) + (beginning-of-line) + (insert "\n") + (forward-char -1)) + ;; Add the new target sources macro (if needed) + (if (project-am-macro ot) + (makefile-insert-macro (project-am-macro ot))) + ;; Add to the list of objects. + (goto-char (point-min)) + (makefile-move-to-macro (car (cdr (cdr ntype)))) + (if (= (point) (point-min)) + (progn + (if (re-search-forward makefile-macroassign-regex nil t) + (progn (forward-line -1) + (end-of-line) + (insert "\n")) + ;; If the above search fails, thats ok. We'd just want to be at + ;; point-min anyway. + ) + (makefile-insert-macro (car (cdr (cdr ntype)))))) + (makefile-end-of-command) + (insert " " (ede-target-name ot)) + (save-buffer) + ;; Rescan the object in this makefile. + (project-rescan ede-object)))) + +;(defun project-am-rescan-toplevel () +; "Rescan all projects in which the current buffer resides." +; (interactive) +; (let* ((tlof (project-am-find-topmost-level default-directory)) +; (tlo (project-am-load tlof)) +; (ede-deep-rescan t)) ; scan deep in this case. +; ;; tlo is the top level object for whatever file we are in +; ;; or nil. If we have an object, call the rescan method. +; (if tlo (project-am-rescan tlo)))) + +;; +;; NOTE TO SELF +;; +;; This should be handled at the EDE level, calling a method of the +;; top most project. +;; +(defmethod project-compile-project ((obj project-am-target) &optional command) + "Compile the entire current project. +Argument COMMAND is the command to use when compiling." + (require 'compile) + (if (not command) + (setq + command + ;; This interactive statement was taken from compile, and I'll + ;; use the same command history too. + (progn + (if (not project-am-compile-project-command) + (setq project-am-compile-project-command compile-command)) + (if (or compilation-read-command current-prefix-arg) + (read-from-minibuffer "Project compile command: " + ;; hardcode make -k + ;; This is compile project after all. + project-am-compile-project-command + nil nil '(compile-history . 1)) + project-am-compile-project-command)))) + ;; When compile a project, we might be in a subdirectory, + ;; so we have to make sure we move all the way to the top. + (let* ((default-directory (project-am-find-topmost-level default-directory))) + (compile command))) + +(defmethod project-compile-project ((obj project-am-makefile) + &optional command) + "Compile the entire current project. +Argument COMMAND is the command to use when compiling." + (require 'compile) + (if (not command) + (setq + command + ;; This interactive statement was taken from compile, and I'll + ;; use the same command history too. + (progn + (if (not project-am-compile-project-command) + (setq project-am-compile-project-command compile-command)) + (if (or compilation-read-command current-prefix-arg) + (read-from-minibuffer "Project compile command: " + ;; hardcode make -k + ;; This is compile project after all. + project-am-compile-project-command + nil nil '(compile-history . 1)) + project-am-compile-project-command)))) + ;; When compile a project, we might be in a subdirectory, + ;; so we have to make sure we move all the way to the top. + (let* ((default-directory (project-am-find-topmost-level default-directory))) + (compile command))) + +(defmethod project-compile-target ((obj project-am-target) &optional command) + "Compile the current target. +Argument COMMAND is the command to use for compiling the target." + (require 'compile) + (if (not project-am-compile-project-command) + (setq project-am-compile-project-command compile-command)) + (if (not command) + (setq + command + (if compilation-read-command + (read-from-minibuffer "Project compile command: " + ;; hardcode make -k + ;; This is compile project after all. + (if ede-object + (format + project-am-compile-target-command + (project-compile-target-command + ede-object)) + project-am-compile-target-command) + nil nil + '(compile-history . 1)) + (if ede-object + project-am-compile-project-command + (format + project-am-compile-target-command + (project-compile-target-command ede-object)))))) + ;; We better be in the right place when compiling a specific target. + (compile command)) + +(defmethod project-debug-target ((obj project-am-objectcode)) + "Run the current project target in a debugger." + (let ((tb (get-buffer-create " *padt*")) + (dd (oref obj path)) + (cmd nil)) + (unwind-protect + (progn + (set-buffer tb) + (setq default-directory dd) + (setq cmd (read-from-minibuffer + "Run (like this): " + (concat (symbol-name project-am-debug-target-function) + " " (ede-target-name obj)))) + (funcall project-am-debug-target-function cmd)) + (kill-buffer tb)))) + +(defmethod project-make-dist ((this project-am-target)) + "Run the current project in the debugger." + (require 'compile) + (if (not project-am-compile-project-command) + (setq project-am-compile-project-command compile-command)) + (project-compile-project this (concat project-am-compile-project-command + " dist"))) + +;;; Project loading and saving +;; +(defun project-am-load (project &optional rootproj) + "Read an automakefile PROJECT into our data structure. +Make sure that the tree down to our makefile is complete so that there +is cohesion in the project. Return the project file (or sub-project). +If a given set of projects has already been loaded, then do nothing +but return the project for the directory given. +Optional ROOTPROJ is the root EDE project." + ;; @TODO - rationalize this to the newer EDE way of doing things. + (setq project (expand-file-name project)) + (let* ((ede-constructing t) + (fn (project-am-find-topmost-level (file-name-as-directory project))) + (amo nil) + (trimmed (if (string-match (regexp-quote fn) + project) + (replace-match "" t t project) + "")) + (subdir nil)) + (setq amo (object-assoc (expand-file-name "Makefile.am" fn) + 'file ede-projects)) + (if amo + (error "Synchronous error in ede/project-am objects") + (let ((project-am-constructing t)) + (setq amo (project-am-load-makefile fn)))) + (if (not amo) + nil + ;; Now scan down from amo, and find the current directory + ;; from the PROJECT file. + (while (< 0 (length trimmed)) + (if (string-match "\\([a-zA-Z0-9.-]+\\)/" trimmed) + (setq subdir (match-string 0 trimmed) + trimmed (replace-match "" t t trimmed)) + (error "Error scanning down path for project")) + (setq amo (project-am-subtree + amo + (expand-file-name "Makefile.am" + (expand-file-name subdir fn))) + fn (expand-file-name subdir fn))) + amo) + )) + +(defun project-am-find-topmost-level (dir) + "Find the topmost automakefile starting with DIR." + (let ((newdir dir)) + (while (or (file-exists-p (concat newdir "Makefile.am")) + (file-exists-p (concat newdir "configure.ac")) + (file-exists-p (concat newdir "configure.in")) + ) + (setq dir newdir newdir + (file-name-directory (directory-file-name newdir)))) + (expand-file-name dir))) + +(defmacro project-am-with-makefile-current (dir &rest forms) + "Set the Makefile.am in DIR to be the current buffer. +Run FORMS while the makefile is current. +Kill the makefile if it was not loaded before the load." + `(let* ((fn (expand-file-name "Makefile.am" ,dir)) + (fb nil) + (kb (get-file-buffer fn))) + (if (not (file-exists-p fn)) + nil + (save-excursion + (if kb (setq fb kb) + ;; We need to find-file this thing, but don't use + ;; any semantic features. + (let ((semantic-init-hooks nil)) + (setq fb (find-file-noselect fn))) + ) + (set-buffer fb) + (prog1 ,@forms + (if (not kb) (kill-buffer (current-buffer)))))))) +(put 'project-am-with-makefile-current 'lisp-indent-function 1) + +(add-hook 'edebug-setup-hook + (lambda () + (def-edebug-spec project-am-with-makefile-current + (form def-body)))) + + +(defun project-am-load-makefile (path) + "Convert PATH into a project Makefile, and return its project object. +It does not check for existing project objects. Use `project-am-load'." + (project-am-with-makefile-current path + (if (and ede-object (project-am-makefile-p ede-object)) + ede-object + (let* ((pi (project-am-package-info path)) + (pn (or (nth 0 pi) (project-am-last-dir fn))) + (ver (or (nth 1 pi) "0.0")) + (bug (nth 2 pi)) + (cof (nth 3 pi)) + (ampf (project-am-makefile + pn :name pn + :version ver + :mailinglist (or bug "") + :file fn))) + (oset ampf :directory (file-name-directory fn)) + (oset ampf configureoutputfiles cof) + (make-local-variable 'ede-object) + (setq ede-object ampf) + ;; Move the rescan after we set ede-object to prevent recursion + (project-rescan ampf) + ampf)))) + +;;; Methods: +(defmethod ede-find-target ((amf project-am-makefile) buffer) + "Fetch the target belonging to BUFFER." + (or (call-next-method) + (let ((targ (oref amf targets)) + (sobj (oref amf subproj)) + (obj nil)) + (while (and targ (not obj)) + (if (ede-buffer-mine (car targ) buffer) + (setq obj (car targ))) + (setq targ (cdr targ))) + (while (and sobj (not obj)) + (setq obj (project-am-buffer-object (car sobj) buffer) + sobj (cdr sobj))) + obj))) + +(defmethod project-targets-for-file ((proj project-am-makefile)) + "Return a list of targets the project PROJ." + (oref proj targets)) + +(defun project-am-scan-for-targets (currproj dir) + "Scan the current Makefile.am for targets. +CURRPROJ is the current project being scanned. +DIR is the directory to apply to new targets." + (let* ((otargets (oref currproj targets)) + (ntargets nil) + (tmp nil) + ) + (mapc + ;; Map all the different types + (lambda (typecar) + (let ((macro (nth 2 typecar)) + (class (nth 1 typecar)) + (indirect (nth 3 typecar)) + ;(name (car typecar)) + ) + (if indirect + ;; Map all the found objects + (mapc (lambda (lstcar) + (setq tmp (object-assoc lstcar 'name otargets)) + (when (not tmp) + (setq tmp (apply class lstcar :name lstcar + :path dir nil))) + (project-rescan tmp) + (setq ntargets (cons tmp ntargets))) + (makefile-macro-file-list macro)) + ;; Non-indirect will have a target whos sources + ;; are actual files, not names of other targets. + (let ((files (makefile-macro-file-list macro))) + (when files + (setq tmp (object-assoc macro 'name otargets)) + (when (not tmp) + (setq tmp (apply class macro :name macro + :path dir nil))) + (project-rescan tmp) + (setq ntargets (cons tmp ntargets)) + )) + ) + )) + project-am-type-alist) + ntargets)) + +(defmethod project-rescan ((this project-am-makefile)) + "Rescan the makefile for all targets and sub targets." + (project-am-with-makefile-current (file-name-directory (oref this file)) + ;;(message "Scanning %s..." (oref this file)) + (let* ((pi (project-am-package-info (oref this directory))) + (pn (nth 0 pi)) + (pv (nth 1 pi)) + (bug (nth 2 pi)) + (cof (nth 3 pi)) + (osubproj (oref this subproj)) + (csubproj (or + ;; If DIST_SUBDIRS doesn't exist, then go for the + ;; static list of SUBDIRS. The DIST version should + ;; contain SUBDIRS plus extra stuff. + (makefile-macro-file-list "DIST_SUBDIRS") + (makefile-macro-file-list "SUBDIRS"))) + (csubprojexpanded nil) + (nsubproj nil) + ;; Targets are excluded here because they require + ;; special attention. + (dir (expand-file-name default-directory)) + (tmp nil) + (ntargets (project-am-scan-for-targets this dir)) + ) + + (and pn (string= (directory-file-name + (oref this directory)) + (directory-file-name + (project-am-find-topmost-level + (oref this directory)))) + (oset this name pn) + (and pv (oset this version pv)) + (and bug (oset this mailinglist bug)) + (oset this configureoutputfiles cof)) + +; ;; LISP is different. Here there is only one kind of lisp (that I know of +; ;; anyway) so it doesn't get mapped when it is found. +; (if (makefile-move-to-macro "lisp_LISP") +; (let ((tmp (project-am-lisp "lisp" +; :name "lisp" +; :path dir))) +; (project-rescan tmp) +; (setq ntargets (cons tmp ntargets)))) +; + ;; Now that we have this new list, chuck the old targets + ;; and replace it with the new list of targets I just created. + (oset this targets (nreverse ntargets)) + ;; We still have a list of targets. For all buffers, make sure + ;; their object still exists! + + ;; FIGURE THIS OUT + + (mapc (lambda (sp) + (let ((var (makefile-extract-varname-from-text sp)) + ) + (if (not var) + (setq csubprojexpanded (cons sp csubprojexpanded)) + ;; If it is a variable, expand that variable, and keep going. + (let ((varexp (makefile-macro-file-list var))) + (dolist (V varexp) + (setq csubprojexpanded (cons V csubprojexpanded))))) + )) + csubproj) + + ;; Ok, now lets look at all our sub-projects. + (mapc (lambda (sp) + (let* ((subdir (file-name-as-directory + (expand-file-name + sp (file-name-directory (oref this :file))))) + (submake (expand-file-name + "Makefile.am" + subdir))) + (if (string= submake (oref this :file)) + nil ;; don't recurse.. please! + + ;; For each project id found, see if we need to recycle, + ;; and if we do not, then make a new one. Check the deep + ;; rescan value for behavior patterns. + (setq tmp (object-assoc + submake + 'file osubproj)) + (if (not tmp) + (setq tmp + (condition-case nil + ;; In case of problem, ignore it. + (project-am-load-makefile subdir) + (error nil))) + ;; If we have tmp, then rescan it only if deep mode. + (if ede-deep-rescan + (project-rescan tmp))) + ;; Tac tmp onto our list of things to keep, but only + ;; if tmp was found. + (when tmp + ;;(message "Adding %S" (object-print tmp)) + (setq nsubproj (cons tmp nsubproj))))) + ) + (nreverse csubprojexpanded)) + (oset this subproj nsubproj) + ;; All elements should be updated now. + ))) + + +(defmethod project-rescan ((this project-am-program)) + "Rescan object THIS." + (oset this :source (makefile-macro-file-list (project-am-macro this))) + (oset this :ldadd (makefile-macro-file-list + (concat (oref this :name) "_LDADD")))) + +(defmethod project-rescan ((this project-am-lib)) + "Rescan object THIS." + (oset this :source (makefile-macro-file-list (project-am-macro this)))) + +(defmethod project-rescan ((this project-am-texinfo)) + "Rescan object THIS." + (oset this :include (makefile-macro-file-list (project-am-macro this)))) + +(defmethod project-rescan ((this project-am-man)) + "Rescan object THIS." + (oset this :source (makefile-macro-file-list (project-am-macro this)))) + +(defmethod project-rescan ((this project-am-lisp)) + "Rescan the lisp sources." + (oset this :source (makefile-macro-file-list (project-am-macro this)))) + +(defmethod project-rescan ((this project-am-header)) + "Rescan the Header sources for object THIS." + (oset this :source (makefile-macro-file-list (project-am-macro this)))) + +(defmethod project-rescan ((this project-am-built-src)) + "Rescan built sources for object THIS." + (oset this :source (makefile-macro-file-list "BUILT_SOURCES"))) + +(defmethod project-rescan ((this project-am-extra-dist)) + "Rescan object THIS." + (oset this :source (makefile-macro-file-list "EXTRA_DIST"))) + ;; NOTE: The below calls 'file' then checks that it is some sort of + ;; text file. The file command may not be available on all platforms + ;; and some files may not exist yet. (ie - auto-generated) + + ;;(mapc + ;; (lambda (f) + ;; ;; prevent garbage to be parsed, could we use :aux ? + ;; (if (and (not (member f (oref this :source))) + ;; (string-match-p "ASCII\\|text" + ;; (shell-command-to-string + ;; (concat "file " f)))) + ;; (oset this :source (cons f (oref this :source))))) + ;; (makefile-macro-file-list "EXTRA_DIST"))) + +(defmethod project-am-macro ((this project-am-objectcode)) + "Return the default macro to 'edit' for this object type." + (concat (subst-char-in-string ?- ?_ (oref this :name)) "_SOURCES")) + +(defmethod project-am-macro ((this project-am-header-noinst)) + "Return the default macro to 'edit' for this object." + "noinst_HEADERS") + +(defmethod project-am-macro ((this project-am-header-inst)) + "Return the default macro to 'edit' for this object." + "include_HEADERS") + +(defmethod project-am-macro ((this project-am-header-pkg)) + "Return the default macro to 'edit' for this object." + "pkginclude_HEADERS") + +(defmethod project-am-macro ((this project-am-header-chk)) + "Return the default macro to 'edit' for this object." + "check_HEADERS") + +(defmethod project-am-macro ((this project-am-texinfo)) + "Return the default macro to 'edit' for this object type." + (concat (file-name-sans-extension (oref this :name)) "_TEXINFOS")) + +(defmethod project-am-macro ((this project-am-man)) + "Return the default macro to 'edit' for this object type." + (oref this :name)) + +(defmethod project-am-macro ((this project-am-lisp)) + "Return the default macro to 'edit' for this object." + "lisp_LISP") + +(defun project-am-buffer-object (amf buffer) + "Return an object starting with AMF associated with BUFFER. +nil means that this buffer belongs to no-one." + (if (not amf) + nil + (if (ede-buffer-mine amf buffer) + amf + (let ((targ (oref amf targets)) + (sobj (oref amf subproj)) + (obj nil)) + (while (and targ (not obj)) + (if (ede-buffer-mine (car targ) buffer) + (setq obj (car targ))) + (setq targ (cdr targ))) + (while (and sobj (not obj)) + (setq obj (project-am-buffer-object (car sobj) buffer) + sobj (cdr sobj))) + obj)))) + +(defmethod ede-buffer-mine ((this project-am-makefile) buffer) + "Return t if object THIS lays claim to the file in BUFFER." + (let ((efn (expand-file-name (buffer-file-name buffer)))) + (or (string= (oref this :file) efn) + (string-match "/configure\\.ac$" efn) + (string-match "/configure\\.in$" efn) + (string-match "/configure$" efn) + ;; Search output files. + (let ((ans nil)) + (dolist (f (oref this configureoutputfiles)) + (when (string-match (concat (regexp-quote f) "$") efn) + (setq ans t))) + ans) + ))) + +(defmethod ede-buffer-mine ((this project-am-objectcode) buffer) + "Return t if object THIS lays claim to the file in BUFFER." + (member (file-name-nondirectory (buffer-file-name buffer)) + (oref this :source))) + +(defmethod ede-buffer-mine ((this project-am-texinfo) buffer) + "Return t if object THIS lays claim to the file in BUFFER." + (let ((bfn (buffer-file-name buffer))) + (or (string= (oref this :name) (file-name-nondirectory bfn)) + (member (file-name-nondirectory bfn) (oref this :include))))) + +(defmethod ede-buffer-mine ((this project-am-man) buffer) + "Return t if object THIS lays claim to the file in BUFFER." + (string= (oref this :name) (buffer-file-name buffer))) + +(defmethod ede-buffer-mine ((this project-am-lisp) buffer) + "Return t if object THIS lays claim to the file in BUFFER." + (member (file-name-nondirectory (buffer-file-name buffer)) + (oref this :source))) + +(defmethod project-am-subtree ((ampf project-am-makefile) subdir) + "Return the sub project in AMPF specified by SUBDIR." + (object-assoc (expand-file-name subdir) 'file (oref ampf subproj))) + +(defmethod project-compile-target-command ((this project-am-target)) + "Default target to use when compiling a given target." + ;; This is a pretty good default for most. + "") + +(defmethod project-compile-target-command ((this project-am-objectcode)) + "Default target to use when compiling an object code target." + (oref this :name)) + +(defmethod project-compile-target-command ((this project-am-texinfo)) + "Default target t- use when compling a texinfo file." + (let ((n (oref this :name))) + (if (string-match "\\.texi?\\(nfo\\)?" n) + (setq n (replace-match ".info" t t n))) + n)) + + +;;; Generic useful functions + +(defun project-am-last-dir (file) + "Return the last part of a directory name. +Argument FILE is the file to extract the end directory name from." + (let* ((s (file-name-directory file)) + (d (directory-file-name s)) + ) + (file-name-nondirectory d)) + ) + +(defun project-am-preferred-target-type (file) + "For FILE, return the preferred type for that file." + (cond ((string-match "\\.texi?\\(nfo\\)$" file) + project-am-texinfo) + ((string-match "\\.[0-9]$" file) + project-am-man) + ((string-match "\\.el$" file) + project-am-lisp) + (t + project-am-program))) + +(defmethod ede-buffer-header-file((this project-am-objectcode) buffer) + "There are no default header files." + (or (call-next-method) + (let ((s (oref this source)) + (found nil)) + (while (and s (not found)) + ;; Add more logic here if applicable. + (if (string-match "\\.\\(h\\|H\\|hh\\|hpp\\)" (car s)) + (setq found (car s))) + (setq s (cdr s))) + found))) + +(defmethod ede-documentation ((this project-am-texinfo)) + "Return a list of files that provides documentation. +Documentation is not for object THIS, but is provided by THIS for other +files in the project." + (let* ((src (append (oref this source) + (oref this include))) + (proj (ede-target-parent this)) + (dir (oref proj directory)) + (out nil)) + ;; Loop over all entries and expand + (while src + (setq out (cons + (expand-file-name (car src) dir) + out)) + (setq src (cdr src))) + ;; return it + out)) + + +;;; Configure.in queries. +;; +(defvar project-am-autoconf-file-options + '("configure.in" "configure.ac") + "List of possible configure files to look in for project info.") + +(defun project-am-autoconf-file (dir) + "Return the name of the autoconf file to use in DIR." + (let ((ans nil)) + (dolist (L project-am-autoconf-file-options) + (when (file-exists-p (expand-file-name L dir)) + (setq ans (expand-file-name L dir)))) + ans)) + +(defmacro project-am-with-config-current (file &rest forms) + "Set the Configure FILE in the top most directory above DIR as current. +Run FORMS in the configure file. +Kill the Configure buffer if it was not already in a buffer." + `(save-excursion + (let ((fb (generate-new-buffer ,file))) + (set-buffer fb) + (erase-buffer) + (insert-file-contents ,file) + (prog1 ,@forms + (kill-buffer fb))))) + +(put 'project-am-with-config-current 'lisp-indent-function 1) + +(add-hook 'edebug-setup-hook + (lambda () + (def-edebug-spec project-am-with-config-current + (form def-body)))) + +(defmacro project-am-extract-shell-variable (var) + "Extract the value of the shell variable VAR from a shell script." + (save-excursion + (goto-char (point-min)) + (when (re-search-forward (concat "^" (regexp-quote var) "\\s-*=\\s-*") + nil t) + (buffer-substring-no-properties (point) (point-at-eol))))) + +(defun project-am-extract-package-info (dir) + "Extract the package information for directory DIR." + (let ((conf-in (project-am-autoconf-file dir)) + (conf-sh (expand-file-name "configure" dir)) + (name (file-name-nondirectory + (directory-file-name dir))) + (ver "1.0") + (bugrep nil) + (configfiles nil) + ) + (cond + ;; Try configure.in or configure.ac + (conf-in + (require 'ede/autoconf-edit) + (project-am-with-config-current conf-in + (let ((aci (autoconf-parameters-for-macro "AC_INIT")) + (aia (autoconf-parameters-for-macro "AM_INIT_AUTOMAKE")) + (acf (autoconf-parameters-for-macro "AC_CONFIG_FILES")) + (aco (autoconf-parameters-for-macro "AC_OUTPUT")) + ) + (cond + ;; AC init has more than 1 parameter + ((> (length aci) 1) + (setq name (nth 0 aci) + ver (nth 1 aci) + bugrep (nth 2 aci))) + ;; The init automake has more than 1 parameter + ((> (length aia) 1) + (setq name (nth 0 aia) + ver (nth 1 aia) + bugrep (nth 2 aia))) + ) + ;; AC_CONFIG_FILES, or AC_OUTPUT lists everything that + ;; should be detected as part of this PROJECT, but not in a + ;; particular TARGET. + (let ((outfiles (cond (aco (list (car aco))) + (t acf)))) + (if (> (length outfiles) 1) + (setq configfiles outfiles) + (setq configfiles (split-string (car outfiles) " " t))) + ) + )) + ) + ;; Else, try the script + ((file-exists-p conf-sh) + (project-am-with-config-current conf-sh + (setq name (project-am-extract-shell-variable "PACKAGE_NAME") + ver (project-am-extract-shell-variable "PACKAGE_VERSION") + ) + )) + ;; Don't know what else.... + (t + nil)) + ;; Return stuff + (list name ver bugrep configfiles) + )) + +(defun project-am-package-info (dir) + "Get the package information for directory topmost project dir over DIR. +Calcultes the info with `project-am-extract-package-info'." + (let ((top (ede-toplevel))) + (when top (setq dir (oref top :directory))) + (project-am-extract-package-info dir))) + +(provide 'ede/project-am) + +;;; ede/project-am.el ends here diff --git a/lisp/cedet/ede/simple.el b/lisp/cedet/ede/simple.el new file mode 100644 index 00000000000..b36a942f3f2 --- /dev/null +++ b/lisp/cedet/ede/simple.el @@ -0,0 +1,108 @@ +;;; ede/simple.el --- Overlay an EDE structure on an existing project + +;; Copyright (C) 2007, 2008, 2009 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: +;; +;; A vast majority of projects use non-EDE project techniques, such +;; as hand written Makefiles, or other IDE's. +;; +;; The EDE-SIMPLE project type allows EDE to wrap an existing mechanism +;; with minimal configuration, and then provides project-root +;; information to Semantic or other tools, and also provides structure +;; information for in-project include header discovery, or speedbar +;; support. +;; +;; It will also support a the minimal EDE UI for compilation and +;; configuration. + +;; @todo - Add support for cpp-root as an ede-simple project. +;; @todo - Allow ede-simple to store locally. + +(require 'ede) +(require 'cedet-files) + +;;; Code: + +(defcustom ede-simple-save-directory "~/.ede" + "*Directory where simple EDE project overlays are saved." + :group 'ede + :type 'directory) + +(defcustom ede-simple-save-file-name "ProjSimple.ede" + "*File name used for simple project wrappers." + :group 'ede + :type 'string) + +(defun ede-simple-projectfile-for-dir (&optional dir) + "Return a full file name to the project file stored in the current directory. +The directory has three parts: + <STORAGE ROOT>/<PROJ DIR AS FILE>/ProjSimple.ede" + (let ((d (or dir default-directory))) + (concat + ;; Storage root + (file-name-as-directory (expand-file-name ede-simple-save-directory)) + ;; Convert directory to filename + (cedet-directory-name-to-file-name d) + ;; Filename + ede-simple-save-file-name) + )) + +(defun ede-simple-load (dir &optional rootproj) + "Load a project of type `Simple' for the directory DIR. +Return nil if there isn't one. +ROOTPROJ is nil, since we will only create a single EDE project here." + (let ((pf (ede-simple-projectfile-for-dir dir)) + (obj nil)) + (when pf + (setq obj (eieio-persistent-read pf)) + (oset obj :directory dir) + ) + obj)) + +(defclass ede-simple-target (ede-target) + () + "EDE Simple project target. +All directories need at least one target.") + +(defclass ede-simple-project (ede-project eieio-persistent) + ((extension :initform ".ede") + (file-header-line :initform ";; EDE Simple Project") + ) + "EDE Simple project class. +Each directory needs a a project file to control it.") + +(defmethod ede-commit-project ((proj ede-simple-project)) + "Commit any change to PROJ to its file." + (when (not (file-exists-p ede-simple-save-directory)) + (if (y-or-n-p (concat ede-simple-save-directory + " doesn't exist. Create? ")) + (make-directory ede-simple-save-directory) + (error "No save directory for new project"))) + (eieio-persistent-save proj)) + +(defmethod ede-find-subproject-for-directory ((proj ede-simple-project) + dir) + "Return PROJ, for handling all subdirs below DIR." + proj) + +(provide 'ede/simple) + +;;; ede/simple.el ends here diff --git a/lisp/cedet/ede/source.el b/lisp/cedet/ede/source.el new file mode 100644 index 00000000000..ca566d0225c --- /dev/null +++ b/lisp/cedet/ede/source.el @@ -0,0 +1,170 @@ +;; ede/source.el --- EDE source code object + +;;; Copyright (C) 2000, 2008 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: + +;; Manage different types of source code. A master list of source code types +;; will be maintained, and used to track target objects, what they accept, +;; and what compilers can be used. + +(require 'eieio-base) + +;;; Code: +(defclass ede-sourcecode (eieio-instance-inheritor) + ((name :initarg :name + :type string + :documentation + "The name of this type of source code. +Such as \"C\" or \"Emacs Lisp\"") + (sourcepattern :initarg :sourcepattern + :initform ".*" + :type string + :documentation + "Emacs regexp matching sourcecode this target accepts.") + (auxsourcepattern :initarg :auxsourcepattern + :initform nil + :type (or null string) + :documentation + "Emacs regexp matching auxiliary source code this target accepts. +Aux source are source code files needed for compilation, which are not compiled +themselves.") + (enable-subdirectories :initarg :enable-subdirectories + :initform nil + :type boolean + :documentation + "Non nil if this sourcecode type uses subdirectories. +If sourcecode always lives near the target creating it, this should be nil. +If sourcecode can, or typically lives in a subdirectory of the owning +target, set this to t.") + (garbagepattern :initarg :garbagepattern + :initform nil + :type list + :documentation + "Shell file regexp matching files considered as garbage. +This is a list of items added to an `rm' command when executing a `clean' +type directive.") + ) + "Description of some type of source code. +Objects will use sourcecode objects to define the types of source +that they are willing to use.") + +(defvar ede-sourcecode-list nil + "The master list of all EDE compilers.") + +;;; Methods +;; +(defmethod initialize-instance :AFTER ((this ede-sourcecode) &rest fields) + "Make sure that all ede compiler objects are cached in +`ede-compiler-list'." + (let ((lst ede-sourcecode-list)) + ;; Find an object of the same name. + (while (and lst (not (string= (oref this name) (oref (car lst) name)))) + (setq lst (cdr lst))) + (if lst + ;; Replace old definition + (setcar lst this) + ;; Add to the beginning of the list. + (setq ede-sourcecode-list (cons this ede-sourcecode-list))))) + +(defmethod ede-want-file-p ((this ede-sourcecode) filename) + "Return non-nil if sourcecode definition THIS will take FILENAME." + (or (ede-want-file-source-p this filename) + (ede-want-file-auxiliary-p this filename))) + +(defmethod ede-want-file-source-p ((this ede-sourcecode) filename) + "Return non-nil if THIS will take FILENAME as an auxiliary ." + (let ((case-fold-search nil)) + (string-match (oref this sourcepattern) filename))) + +(defmethod ede-want-file-auxiliary-p ((this ede-sourcecode) filename) + "Return non-nil if THIS will take FILENAME as an auxiliary ." + (let ((case-fold-search nil)) + (and (slot-boundp this 'auxsourcepattern) + (oref this auxsourcepattern) + (string-match (oref this auxsourcepattern) filename)))) + +(defmethod ede-want-any-source-files-p ((this ede-sourcecode) filenames) + "Return non-nil if THIS will accept any source files in FILENAMES." + (let (found) + (while (and (not found) filenames) + (setq found (ede-want-file-source-p this (pop filenames)))))) + +(defmethod ede-want-any-auxiliary-files-p ((this ede-sourcecode) filenames) + "Return non-nil if THIS will accept any aux files in FILENAMES." + (let (found) + (while (and (not found) filenames) + (setq found (ede-want-file-auxiliary-p this (pop filenames)))))) + +(defmethod ede-want-any-files-p ((this ede-sourcecode) filenames) + "Return non-nil if THIS will accept any files in FILENAMES." + (let (found) + (while (and (not found) filenames) + (setq found (ede-want-file-p this (pop filenames)))))) + +(defmethod ede-buffer-header-file ((this ede-sourcecode) filename) + "Return a list of file names of header files for THIS with FILENAME. +Used to guess header files, but uses the auxsource regular expression." + (let ((dn (file-name-directory filename)) + (ts (file-name-sans-extension (file-name-nondirectory filename))) + (ae (oref this auxsourcepattern))) + (if (not ae) + nil + (directory-files dn t (concat (regexp-quote ts) ae))))) + +;;; Utility functions +;; +(when nil + ;; not used at the moment. +(defun ede-source-find (name) + "Find the sourcecode object based on NAME." + (object-assoc name :name ede-sourcecode-list)) + +(defun ede-source-match (file) + "Find the list of soucecode objects which matches FILE." + (let ((lst ede-sourcecode-list) + (match nil)) + (while lst + ;; ede-file-mine doesn't exist yet + (if (ede-file-mine (car lst) file) + (setq match (cons (car lst) match))) + (setq lst (cdr lst))) + match)) +) +;;; Master list of source code types +;; +;; This must appear at the end so that the init method will work. +(defvar ede-source-scheme + (ede-sourcecode "ede-source-scheme" + :name "Scheme" + :sourcepattern "\\.scm$") + "Scheme source code definition.") + +;;(defvar ede-source- +;; (ede-sourcecode "ede-source-" +;; :name "" +;; :sourcepattern "\\.$" +;; :garbagepattern '("*.")) +;; " source code definition.") + +(provide 'ede/source) + +;;; ede/source.el ends here diff --git a/lisp/cedet/ede/speedbar.el b/lisp/cedet/ede/speedbar.el new file mode 100644 index 00000000000..cbd34e944ee --- /dev/null +++ b/lisp/cedet/ede/speedbar.el @@ -0,0 +1,353 @@ +;;; ede/speedbar.el --- Speedbar viewing of EDE projects + +;;; Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005, 2007, 2008, 2009 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Keywords: project, make, tags + +;; 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: +;; +;; Display a project's hierarchy in speedbar. +;; + +;;; Code: +(require 'speedbar) +(require 'eieio-speedbar) +(require 'ede) + +;;; Speedbar support mode +;; +(defvar ede-speedbar-key-map nil + "A Generic object based speedbar display keymap.") + +(defun ede-speedbar-make-map () + "Make the generic object based speedbar keymap." + (setq ede-speedbar-key-map (speedbar-make-specialized-keymap)) + + ;; General viewing things + (define-key ede-speedbar-key-map "\C-m" 'speedbar-edit-line) + (define-key ede-speedbar-key-map "+" 'speedbar-expand-line) + (define-key ede-speedbar-key-map "=" 'speedbar-expand-line) + (define-key ede-speedbar-key-map "-" 'speedbar-contract-line) + (define-key ede-speedbar-key-map " " 'speedbar-toggle-line-expansion) + + ;; Some object based things + (define-key ede-speedbar-key-map "C" 'eieio-speedbar-customize-line) + + ;; Some project based things + (define-key ede-speedbar-key-map "R" 'ede-speedbar-remove-file-from-target) + (define-key ede-speedbar-key-map "b" 'ede-speedbar-compile-line) + (define-key ede-speedbar-key-map "B" 'ede-speedbar-compile-project) + (define-key ede-speedbar-key-map "D" 'ede-speedbar-make-distribution) + (define-key ede-speedbar-key-map "E" 'ede-speedbar-edit-projectfile) + ) + +(defvar ede-speedbar-menu + '([ "Compile" ede-speedbar-compile-line t] + [ "Compile Project" ede-speedbar-compile-project + (ede-project-child-p (speedbar-line-token)) ] + "---" + [ "Edit File/Tag" speedbar-edit-line + (not (eieio-object-p (speedbar-line-token)))] + [ "Expand" speedbar-expand-line + (save-excursion (beginning-of-line) + (looking-at "[0-9]+: *.\\+. "))] + [ "Contract" speedbar-contract-line + (save-excursion (beginning-of-line) + (looking-at "[0-9]+: *.-. "))] + "---" + [ "Remove File from Target" ede-speedbar-remove-file-from-target + (stringp (speedbar-line-token)) ] + [ "Customize Project/Target" eieio-speedbar-customize-line + (eieio-object-p (speedbar-line-token)) ] + [ "Edit Project File" ede-speedbar-edit-projectfile t] + [ "Make Distribution" ede-speedbar-make-distribution + (ede-project-child-p (speedbar-line-token)) ] + ) + "Menu part in easymenu format used in speedbar while browsing objects.") + +(eieio-speedbar-create 'ede-speedbar-make-map + 'ede-speedbar-key-map + 'ede-speedbar-menu + "Project" + 'ede-speedbar-toplevel-buttons) + + +(defun ede-speedbar () + "EDE development environment project browser for speedbar." + (interactive) + (speedbar-frame-mode 1) + (speedbar-change-initial-expansion-list "Project") + (speedbar-get-focus) + ) + +(defun ede-speedbar-toplevel-buttons (dir) + "Return a list of objects to display in speedbar. +Argument DIR is the directory from which to derive the list of objects." + ede-projects + ) + +;;; Some special commands useful in EDE +;; +(defun ede-speedbar-remove-file-from-target () + "Remove the file at point from it's target." + (interactive) + (if (stringp (speedbar-line-token)) + (progn + (speedbar-edit-line) + (ede-remove-file)))) + +(defun ede-speedbar-compile-line () + "Compile/Build the project or target on this line." + (interactive) + (let ((obj (eieio-speedbar-find-nearest-object))) + (if (not (eieio-object-p obj)) + nil + (cond ((obj-of-class-p obj ede-project) + (project-compile-project obj)) + ((obj-of-class-p obj ede-target) + (project-compile-target obj)) + (t (error "Error in speedbar structure")))))) + +(defun ede-speedbar-get-top-project-for-line () + "Return a project object for this line." + (interactive) + (let ((obj (eieio-speedbar-find-nearest-object))) + (if (not (eieio-object-p obj)) + (error "Error in speedbar or ede structure") + (if (obj-of-class-p obj ede-target) + (setq obj (ede-target-parent obj))) + (if (obj-of-class-p obj ede-project) + obj + (error "Error in speedbar or ede structure"))))) + +(defun ede-speedbar-compile-project () + "Compile/Build the project which owns this line." + (interactive) + (project-compile-project (ede-speedbar-get-top-project-for-line))) + +(defun ede-speedbar-compile-file-project () + "Compile/Build the target which the current file belongs to." + (interactive) + (let* ((file (speedbar-line-file)) + (buf (find-file-noselect file)) + (bwin (get-buffer-window buf 0))) + (if bwin + (progn + (select-window bwin) + (raise-frame (window-frame bwin))) + (dframe-select-attached-frame speedbar-frame) + (set-buffer buf) + (ede-compile-target)))) + +(defun ede-speedbar-make-distribution () + "Edit the project file based on this line." + (interactive) + (project-make-dist (ede-speedbar-get-top-project-for-line))) + +(defun ede-speedbar-edit-projectfile () + "Edit the project file based on this line." + (interactive) + (project-edit-file-target (ede-speedbar-get-top-project-for-line))) + +;;; Speedbar Project Methods +;; +(defun ede-find-nearest-file-line () + "Go backwards until we find a file." + (save-excursion + (beginning-of-line) + (looking-at "^\\([0-9]+\\):") + (let ((depth (string-to-number (match-string 1)))) + (while (not (re-search-forward "[]] [^ ]" + (save-excursion (end-of-line) + (point)) + t)) + (re-search-backward (format "^%d:" (1- depth))) + (setq depth (1- depth))) + (speedbar-line-token)))) + +(defmethod eieio-speedbar-derive-line-path ((obj ede-project) &optional depth) + "Return the path to OBJ. +Optional DEPTH is the depth we start at." + (file-name-directory (oref obj file)) + ) + +(defmethod eieio-speedbar-derive-line-path ((obj ede-target) &optional depth) + "Return the path to OBJ. +Optional DEPTH is the depth we start at." + (let ((proj (ede-target-parent obj))) + ;; Check the type of line we are currently on. + ;; If we are on a child, we need a file name too. + (save-excursion + (let ((lt (speedbar-line-token))) + (if (or (eieio-object-p lt) (stringp lt)) + (eieio-speedbar-derive-line-path proj) + ;; a child element is a token. Do some work to get a filename too. + (concat (eieio-speedbar-derive-line-path proj) + (ede-find-nearest-file-line))))))) + +(defmethod eieio-speedbar-description ((obj ede-project)) + "Provide a speedbar description for OBJ." + (ede-description obj)) + +(defmethod eieio-speedbar-description ((obj ede-target)) + "Provide a speedbar description for OBJ." + (ede-description obj)) + +(defmethod eieio-speedbar-child-description ((obj ede-target)) + "Provide a speedbar description for a plain-child of OBJ. +A plain child is a child element which is not an EIEIO object." + (or (speedbar-item-info-file-helper) + (speedbar-item-info-tag-helper))) + +(defmethod eieio-speedbar-object-buttonname ((object ede-project)) + "Return a string to use as a speedbar button for OBJECT." + (if (ede-parent-project object) + (ede-name object) + (concat (ede-name object) " " (oref object version)))) + +(defmethod eieio-speedbar-object-buttonname ((object ede-target)) + "Return a string to use as a speedbar button for OBJECT." + (ede-name object)) + +(defmethod eieio-speedbar-object-children ((this ede-project)) + "Return the list of speedbar display children for THIS." + (condition-case nil + (with-slots (subproj targets) this + (append subproj targets)) + (error nil))) + +(defmethod eieio-speedbar-object-children ((this ede-target)) + "Return the list of speedbar display children for THIS." + (oref this source)) + +(defmethod eieio-speedbar-child-make-tag-lines ((this ede-target) depth) + "Create a speedbar tag line for a child of THIS. +It has depth DEPTH." + (with-slots (source) this + (mapcar (lambda (car) + (speedbar-make-tag-line 'bracket ?+ + 'speedbar-tag-file + car + car + 'ede-file-find + car + 'speedbar-file-face depth)) + source))) + +;;; Generic file management for TARGETS +;; +(defun ede-file-find (text token indent) + "Find the file TEXT at path TOKEN. +INDENT is the current indentation level." + (speedbar-find-file-in-frame + (expand-file-name token (speedbar-line-directory indent))) + (speedbar-maybee-jump-to-attached-frame)) + +(defun ede-create-tag-buttons (filename indent) + "Create the tag buttons associated with FILENAME at INDENT." + (let* ((lst (speedbar-fetch-dynamic-tags filename))) + ;; if no list, then remove expando button + (if (not lst) + (speedbar-change-expand-button-char ??) + (speedbar-with-writable + ;; We must do 1- because indent was already incremented. + (speedbar-insert-generic-list (1- indent) + lst + 'ede-tag-expand + 'ede-tag-find))))) + +(defun ede-tag-expand (text token indent) + "Expand a tag sublist. Imenu will return sub-lists of specialized tag types. +Etags does not support this feature. TEXT will be the button +string. TOKEN will be the list, and INDENT is the current indentation +level." + (cond ((string-match "+" text) ;we have to expand this file + (speedbar-change-expand-button-char ?-) + (speedbar-with-writable + (save-excursion + (end-of-line) (forward-char 1) + (speedbar-insert-generic-list indent token + 'ede-tag-expand + 'ede-tag-find)))) + ((string-match "-" text) ;we have to contract this node + (speedbar-change-expand-button-char ?+) + (speedbar-delete-subblock indent)) + (t (error "Ooops... not sure what to do"))) + (speedbar-center-buffer-smartly)) + +(defun ede-tag-find (text token indent) + "For the tag TEXT in a file TOKEN, goto that position. +INDENT is the current indentation level." + (let ((file (ede-find-nearest-file-line))) + (speedbar-find-file-in-frame file) + (save-excursion (speedbar-stealthy-updates)) + ;; Reset the timer with a new timeout when cliking a file + ;; in case the user was navigating directories, we can cancel + ;; that other timer. +; (speedbar-set-timer speedbar-update-speed) + (goto-char token) + (run-hooks 'speedbar-visiting-tag-hook) + ;;(recenter) + (speedbar-maybee-jump-to-attached-frame) + )) + +;;; EDE and the speedbar FILE display +;; +;; This will add a couple keybindings and menu items into the +;; FILE display for speedbar. + +(defvar ede-speedbar-file-menu-additions + '("----" + ["Create EDE Target" ede-new-target (ede-current-project) ] + ["Add to project" ede-speedbar-file-add-to-project (ede-current-project) ] + ["Compile project" ede-speedbar-compile-project (ede-current-project) ] + ["Compile file target" ede-speedbar-compile-file-target (ede-current-project) ] + ["Make distribution" ede-make-dist (ede-current-project) ] + ) + "Set of menu items to splice into the speedbar menu.") + +(defvar ede-speedbar-file-keymap + (let ((km (make-sparse-keymap))) + (define-key km "a" 'ede-speedbar-file-add-to-project) + (define-key km "t" 'ede-new-target) + (define-key km "s" 'ede-speedbar) + (define-key km "C" 'ede-speedbar-compile-project) + (define-key km "c" 'ede-speedbar-compile-file-target) + (define-key km "d" 'ede-make-dist) + km) + "Keymap spliced into the speedbar keymap.") + +(defun ede-speedbar-file-setup () + "Setup some keybindings in the Speedbar File display." + (setq speedbar-easymenu-definition-special + (append speedbar-easymenu-definition-special + ede-speedbar-file-menu-additions + )) + (define-key speedbar-file-key-map "." ede-speedbar-file-keymap) + ;; Finally, if the FILES mode is loaded, force a refresh + ;; of the menus and such. + (when (and (string= speedbar-initial-expansion-list-name "files") + (buffer-live-p speedbar-buffer) + ) + (speedbar-change-initial-expansion-list "files"))) + +(provide 'ede/speedbar) + +;;; ede/speedbar.el ends here diff --git a/lisp/cedet/ede/system.el b/lisp/cedet/ede/system.el new file mode 100644 index 00000000000..e3699b19c24 --- /dev/null +++ b/lisp/cedet/ede/system.el @@ -0,0 +1,137 @@ +;;; ede-system.el --- EDE working with the system (VC, FTP, ETC) + +;;; Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Keywords: project, make, vc + +;; 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: +;; +;; EDE system contains some routines to work with EDE projects saved in +;; CVS repositories, and services such as sourceforge which lets you +;; perform releases via FTP. + +(require 'ede) + +;;; Code: + +;;; Web/FTP site node. +;; +(defun ede-web-browse-home () + "Browse the home page of the current project." + (interactive) + (if (not (ede-toplevel)) + (error "No project")) + (let ((home (oref (ede-toplevel) web-site-url))) + (if (string= "" home) + (error "Now URL is stored in this project")) + (require 'browse-url) + (browse-url home) + )) + + +(defun ede-edit-web-page () + "Edit the web site for this project." + (interactive) + (let* ((toplevel (ede-toplevel)) + (dir (oref toplevel web-site-directory)) + (file (oref toplevel web-site-file)) + (endfile (concat (file-name-as-directory dir) file))) + (if (string-match "^/r[:@]" endfile) + (require 'tramp)) + (when (not (file-exists-p endfile)) + (setq endfile file) + (if (string-match "^/r[:@]" endfile) + (require 'tramp)) + (if (not (file-exists-p endfile)) + (error "No project file found"))) + (find-file endfile))) + + +(defun ede-upload-distribution () + "Upload the current distribution to the correct location. +Use /user@ftp.site.com: file names for FTP sites. +Download tramp, and use /r:machine: for names on remote sites w/out FTP access." + (interactive) + (let* ((files (project-dist-files (ede-toplevel))) + (upload (if (string= (oref (ede-toplevel) ftp-upload-site) "") + (oref (ede-toplevel) ftp-site) + (oref (ede-toplevel) ftp-upload-site)))) + (when (or (string= upload "") + (not (file-exists-p upload))) + (error "Upload directory %S does not exist" upload)) + (while files + (let ((localfile (concat (file-name-directory (oref (ede-toplevel) file)) + (car files)))) + (if (not (file-exists-p localfile)) + (progn + (message "File %s does not exist yet. Building a distribution" + localfile) + (ede-make-dist) + (error "File %s does not exist yet. Building a distribution" + localfile) + )) + (setq upload + (concat (directory-file-name upload) + "/" + (file-name-nondirectory localfile))) + (copy-file localfile upload) + (setq files (cdr files))))) + (message "Done uploading files...") + ) + +(defun ede-upload-html-documentation () + "Upload the current distributions documentation as HTML. +Use /user@ftp.site.com: file names for FTP sites. +Download tramp, and use /r:machine: for names on remote sites w/out FTP access." + (interactive) + (let* ((files nil) ;(ede-html-doc-files (ede-toplevel))) + (upload (if (string= (oref (ede-toplevel) ftp-upload-site) "") + (oref (ede-toplevel) ftp-site) + (oref (ede-toplevel) ftp-upload-site)))) + (when (or (string= upload "") + (not (file-exists-p upload))) + (error "Upload directory %S does not exist" upload)) + (while files + (let ((localfile (concat (file-name-directory (oref (ede-toplevel) file)) + (car files)))) + (if (not (file-exists-p localfile)) + (progn + (message "File %s does not exist yet. Building a distribution" + localfile) + ;;(project-compile-target ... ) + (error "File %s does not exist yet. Building a distribution" + localfile) + )) + (copy-file localfile upload) + (setq files (cdr files))))) + (message "Done uploading files...") + ) + +;;; Version Control +;; +;; Do a few nice things with Version control systems. +(defun ede-vc-project-directory () + "Run `vc-dir' on the current project." + (interactive) + (let ((top (ede-toplevel-project-or-nil default-directory))) + (vc-dir top nil))) + +(provide 'ede/system) + +;;; ede/system.el ends here diff --git a/lisp/cedet/ede/util.el b/lisp/cedet/ede/util.el new file mode 100644 index 00000000000..efe0c0008f1 --- /dev/null +++ b/lisp/cedet/ede/util.el @@ -0,0 +1,106 @@ +;;; ede/util.el --- EDE utilities + +;;; Copyright (C) 2000, 2005 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: +;; +;; Utilities that may not require project specific help, and oporate +;; on generic EDE structures. Provide user level commands for activities +;; not directly related to source code organization or makefile generation. + +(require 'ede) + +;;; Code: + +;;; Updating the version of a project. +;;;###autoload +(defun ede-update-version (newversion) + "Update the current projects main version number. +Argument NEWVERSION is the version number to use in the current project." + (interactive (list (let* ((o (ede-toplevel)) + (v (oref o version))) + (read-string (format "Update Version (was %s): " v) + v nil v)))) + (let ((ede-object (ede-toplevel))) + ;; Don't update anything if there was no change. + (unless (string= (oref ede-object :version) newversion) + (oset ede-object :version newversion) + (project-update-version ede-object) + (ede-update-version-in-source ede-object newversion)))) + +(defmethod project-update-version ((ot ede-project)) + "The :version of the project OT has been updated. +Handle saving, or other detail." + (error "project-update-version not supported by %s" (object-name ot))) + +(defmethod ede-update-version-in-source ((this ede-project) version) + "Change occurrences of a version string in sources. +In project THIS, cycle over all targets to give them a chance to set +their sources to VERSION." + (ede-map-targets this (lambda (targ) + (ede-update-version-in-source targ version)))) + +(defmethod ede-update-version-in-source ((this ede-target) version) + "In sources for THIS, change version numbers to VERSION." + (if (and (slot-boundp this 'versionsource) + (oref this versionsource)) + (let ((vs (oref this versionsource))) + (while vs + (save-excursion + (set-buffer (find-file-noselect + (ede-expand-filename this (car vs)))) + (goto-char (point-min)) + (let ((case-fold-search t)) + (if (re-search-forward "version:\\s-*\\([^ \t\n]+\\)" nil t) + (progn + (save-match-data + (ede-make-buffer-writable)) + (delete-region (match-beginning 1) + (match-end 1)) + (goto-char (match-beginning 1)) + (insert version))))) + (setq vs (cdr vs)))))) + +;;; Writable files +;; +;; Utils for EDE when it needs to write a file that could be covered by a +;; version control system. +(defun ede-make-buffer-writable (&optional buffer) + "Make sure that BUFFER is writable. +If BUFFER isn't specified, use the current buffer." + (save-excursion + (if buffer (set-buffer buffer)) + (if buffer-read-only + (if (and vc-mode + (y-or-n-p (format "Check out %s? " (buffer-file-name)))) + (vc-toggle-read-only) + (if (not vc-mode) + (toggle-read-only -1)))))) + +(provide 'ede/util) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: ede/loaddefs +;; generated-autoload-load-name: "ede/util" +;; End: + +;;; ede/util.el ends here diff --git a/lisp/cedet/semantic/symref/filter.el b/lisp/cedet/semantic/symref/filter.el index 7052d764417..97e5c92a6ab 100644 --- a/lisp/cedet/semantic/symref/filter.el +++ b/lisp/cedet/semantic/symref/filter.el @@ -34,11 +34,13 @@ ;;; Code: (require 'semantic) +(require 'semantic/analyze) (declare-function srecode-active-template-region "srecode/fields") (declare-function srecode-delete "srecode/fields") (declare-function srecode-field "srecode/fields") (declare-function srecode-template-inserted-region "srecode/fields") (declare-function srecode-overlaid-activate "srecode/fields") +(declare-function semantic-idle-summary-useful-context-p "semantic/idle") ;;; FILTERS ;; @@ -65,6 +67,7 @@ HOOKFCN takes three arguments that match ( START END PREFIX ) Search occurs in the current buffer between START and END." + (require 'semantic/idle) (save-excursion (goto-char start) (let* ((str (semantic-tag-name target)) |