]> code.delx.au - gnu-emacs/blobdiff - lisp/cedet/ede.el
Silence some cedet compilation warnings
[gnu-emacs] / lisp / cedet / ede.el
index 307ccfdadd7c5486ad5278ca76c585ef0acd5f88..5fecd8b994f2a6b7f2c605f576384cd976b41514 100644 (file)
@@ -1,10 +1,10 @@
 ;;; ede.el --- Emacs Development Environment gloss
 
-;; Copyright (C) 1998-2005, 2007-201 Free Software Foundation, Inc.
+;; Copyright (C) 1998-2005, 2007-2013 Free Software Foundation, Inc.
 
 ;; Author: Eric M. Ludlam <zappo@gnu.org>
 ;; Keywords: project, make
-;; Version: 1.0pre7
+;; Version: 1.2
 
 ;; This file is part of GNU Emacs.
 
@@ -60,7 +60,7 @@
 (declare-function ede-up-directory "ede/files")
 (declare-function semantic-lex-make-spp-table "semantic/lex-spp")
 
-(defconst ede-version "1.0"
+(defconst ede-version "1.2"
   "Current version of the Emacs EDE.")
 
 ;;; Code:
@@ -94,6 +94,42 @@ target willing to take the file.  'never means never perform the check."
   :group 'ede
   :type 'sexp) ; make this be a list of options some day
 
+(defcustom ede-project-directories nil
+  "Directories in which EDE may search for project files.
+If the value is t, EDE may search in any directory.
+
+If the value is a function, EDE calls that function with one
+argument, the directory name; the function should return t iff
+EDE should look for project files in the directory.
+
+Otherwise, the value should be a list of fully-expanded directory
+names.  EDE searches for project files only in those directories.
+If you invoke the commands \\[ede] or \\[ede-new] on a directory
+that is not listed, Emacs will offer to add it to the list.
+
+Any other value disables searching for EDE project files."
+  :group 'ede
+  :type '(choice (const :tag "Any directory" t)
+                (repeat :tag "List of directories"
+                        (directory))
+                (function :tag "Predicate"))
+  :version "23.4"
+  :risky t)
+
+(defun ede-directory-safe-p (dir)
+  "Return non-nil if DIR is a safe directory to load projects from.
+Projects that do not load a project definition as Emacs Lisp code
+are safe, and can be loaded automatically.  Other project types,
+such as those created with Project.ede files, are safe only if
+specified by `ede-project-directories'."
+  (setq dir (directory-file-name (expand-file-name dir)))
+  ;; Load only if allowed by `ede-project-directories'.
+  (or (eq ede-project-directories t)
+      (and (functionp ede-project-directories)
+          (funcall ede-project-directories dir))
+      (and (listp ede-project-directories)
+          (member dir ede-project-directories))))
+
 \f
 ;;; Management variables
 
@@ -158,7 +194,6 @@ Argument LIST-O-O is the list of objects to choose from."
     (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)
@@ -214,9 +249,9 @@ Argument LIST-O-O is the list of objects to choose from."
     (and obj (obj-of-class-p obj ede-target))))
 
 (defun ede-buffer-belongs-to-project-p ()
-  "Return non-nil if this buffer belongs to at least one target."
+  "Return non-nil if this buffer belongs to at least one project."
   (if (or (null ede-object) (consp ede-object)) nil
-    (obj-of-class-p ede-object ede-project)))
+    (obj-of-class-p ede-object-project ede-project)))
 
 (defun ede-menu-obj-of-class-p (class)
   "Return non-nil if some member of `ede-object' is a child of CLASS."
@@ -243,7 +278,7 @@ Argument MENU-DEF is the menu definition to use."
              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
+       ;; 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
@@ -264,7 +299,7 @@ Argument MENU-DEF is the menu definition to use."
          (setq targets (cdr targets)))
        ;; Fourth, build sub projects.
        ;; -- nerp
-       ;; Fifth, Add make distribution
+       ;; Fifth, add make distribution
        (append newmenu (list [ "Make distribution" ede-make-dist t ]))
        )))))
 
@@ -295,18 +330,19 @@ Argument MENU-DEF is the menu definition to use."
    (easy-menu-create-menu
     "Project Forms"
     (let* ((obj (ede-current-project))
-          (class (if obj (object-class obj)))
+          (class (if obj (eieio-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))
+                   class (eieio-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 ]
+               ( "Default configuration" :filter ede-configuration-forms-menu )
                "-")
             menu
             ))
@@ -314,6 +350,41 @@ Argument MENU-DEF is the menu definition to use."
               menu)
        )))))
 
+(defun ede-configuration-forms-menu (menu-def)
+  "Create a submenu for selecting the default configuration for this project.
+The current default is in the current object's CONFIGURATION-DEFAULT slot.
+All possible configurations are in CONFIGURATIONS.
+Argument MENU-DEF specifies the menu being created."
+  (easy-menu-filter-return
+   (easy-menu-create-menu
+    "Configurations"
+    (let* ((obj (ede-current-project))
+          (conf (when obj (oref obj configurations)))
+          (cdef (when obj (oref obj configuration-default)))
+          (menu nil))
+      (dolist (C conf)
+       (setq menu (cons (vector C (list 'ede-project-configurations-set C)
+                                :style 'toggle
+                                :selected (string= C cdef))
+                        menu))
+       )
+      (nreverse menu)))))
+
+(defun ede-project-configurations-set (newconfig)
+  "Set the current project's current configuration to NEWCONFIG.
+This function is designed to be used by `ede-configuration-forms-menu'
+but can also be used interactively."
+  (interactive
+   (list (let* ((proj (ede-current-project))
+               (configs (oref proj configurations)))
+          (completing-read "New configuration: "
+                           configs nil t
+                           (oref proj configuration-default)))))
+  (oset (ede-current-project) configuration-default newconfig)
+  (message "%s will now build in %s mode."
+          (eieio-object-name (ede-current-project))
+          newconfig))
+
 (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."
@@ -341,9 +412,14 @@ Argument MENU-DEF is the definition of the current menu."
   "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)))
+  (let ((object (or ede-object ede-selected-object))
+       (proj ede-object-project))
     (condition-case nil
        (let ((keys (ede-object-keybindings object)))
+         ;; Add keys for the project to whatever is in the current object
+         ;; so long as it isn't the same.
+         (when (not (eq object proj))
+           (setq keys (append keys (ede-object-keybindings proj))))
          (while keys
            (local-set-key (concat "\C-c." (car (car keys)))
                           (cdr (car keys)))
@@ -379,8 +455,8 @@ If optional argument CURRENT is non-nil, return sub-menu code."
 
 (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-project-local-variables)
+  ;; Apply keymaps and preprocessor symbols.
   (ede-apply-object-keymap)
   (ede-apply-preprocessor-map)
   )
@@ -398,8 +474,9 @@ To be used in hook functions."
 
 (define-minor-mode ede-minor-mode
   "Toggle EDE (Emacs Development Environment) minor mode.
-With non-nil argument ARG, enable EDE minor mode if ARG is
-positive; otherwise, disable it.
+With a prefix argument ARG, enable EDE minor mode if ARG is
+positive, and disable it otherwise.  If called from Lisp, enable
+EDE minor mode if ARG is omitted or nil.
 
 If this file is contained, or could be contained in an EDE
 controlled project, then this mode is activated automatically
@@ -419,28 +496,46 @@ provided `global-ede-mode' is enabled."
 Sets buffer local variables for EDE."
   (let* ((ROOT nil)
         (proj (ede-directory-get-open-project default-directory
-                                              'ROOT)))
+                                              'ROOT))
+        (projauto nil))
+
     (when (or proj ROOT
-             (ede-directory-project-p default-directory t))
+             ;; If there is no open project, look up the project
+             ;; autoloader to see if we should initialize.
+             (setq projauto (ede-directory-project-p default-directory t)))
+
+      (when (and (not proj) projauto)
+
+       ;; No project was loaded, but we have a project description
+       ;; object.  This means that we can check if it is a safe
+       ;; project to load before requesting it to be loaded.
+
+       (when (or (oref projauto safe-p)
+                 ;; The project style is not safe, so check if it is
+                 ;; in `ede-project-directories'.
+                 (let ((top (ede-toplevel-project default-directory)))
+                   (ede-directory-safe-p top)))
 
-      (when (not proj)
-       ;; @todo - this could be wasteful.
-       (setq proj (ede-load-project-file default-directory 'ROOT)))
+         ;; The project is safe, so load it in.
+         (setq proj (ede-load-project-file default-directory 'ROOT))))
 
-      (setq ede-object (ede-buffer-object (current-buffer)
+      ;; Only initialize EDE state in this buffer if we found a project.
+      (when proj
+
+       (setq ede-object (ede-buffer-object (current-buffer)
                                          'ede-object-project))
 
-      (setq ede-object-root-project
-           (or ROOT (ede-project-root ede-object-project)))
+       (setq ede-object-root-project
+             (or ROOT (ede-project-root ede-object-project)))
 
-      (if (and (not ede-object) ede-object-project)
-         (ede-auto-add-to-target))
+       (if (and (not ede-object) ede-object-project)
+           (ede-auto-add-to-target))
 
-      (ede-apply-target-options))))
+       (ede-apply-target-options)))))
 
-(defun ede-reset-all-buffers (onoff)
-  "Reset all the buffers due to change in EDE.
-ONOFF indicates enabling or disabling the mode."
+(defun ede-reset-all-buffers ()
+  "Reset all the buffers due to change in EDE."
+  (interactive)
   (let ((b (buffer-list)))
     (while b
       (when (buffer-file-name (car b))
@@ -458,8 +553,9 @@ ONOFF indicates enabling or disabling the mode."
 ;;;###autoload
 (define-minor-mode global-ede-mode
   "Toggle global EDE (Emacs Development Environment) mode.
-With non-nil argument ARG, enable global EDE mode if ARG is
-positive; otherwise, disable it.
+With a prefix argument ARG, enable global EDE mode if ARG is
+positive, and disable it otherwise.  If called from Lisp, enable
+the mode if ARG is omitted or nil.
 
 This global minor mode enables `ede-minor-mode' in all buffers in
 an EDE controlled project."
@@ -477,7 +573,7 @@ an EDE controlled project."
        (add-hook 'dired-mode-hook 'ede-turn-on-hook)
        (add-hook 'kill-emacs-hook 'ede-save-cache)
        (ede-load-cache)
-       (ede-reset-all-buffers 1))
+       (ede-reset-all-buffers))
     ;; Turn off global-ede-mode
     (define-key cedet-menu-map [cedet-menu-separator] nil)
     (remove-hook 'semanticdb-project-predicate-functions 'ede-directory-project-p)
@@ -487,7 +583,7 @@ an EDE controlled project."
     (remove-hook 'dired-mode-hook 'ede-turn-on-hook)
     (remove-hook 'kill-emacs-hook 'ede-save-cache)
     (ede-save-cache)
-    (ede-reset-all-buffers -1)))
+    (ede-reset-all-buffers)))
 
 (defvar ede-ignored-file-alist
   '( "\\.cvsignore$"
@@ -510,7 +606,7 @@ an EDE controlled project."
   "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 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
@@ -555,16 +651,75 @@ of objects with the `ede-want-file-p' method."
 \f
 ;;; 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 (dir)
+  "Start up EDE for directory DIR.
+If DIR has an existing project file, load it.
+Otherwise, create a new project for DIR."
+  (interactive
+   ;; When choosing a directory to turn on, and we see some directory here,
+   ;; provide that as the default.
+   (let* ((top (ede-toplevel-project default-directory))
+         (promptdflt (or top default-directory)))
+     (list (read-directory-name "Project directory: "
+                               promptdflt promptdflt t))))
+  (unless (file-directory-p dir)
+    (error "%s is not a directory" dir))
+  (when (ede-directory-get-open-project dir)
+    (error "%s already has an open project associated with it" dir))
+
+  ;; Check if the directory has been added to the list of safe
+  ;; directories.  It can also add the directory to the safe list if
+  ;; the user chooses.
+  (if (ede-check-project-directory dir)
+      (progn
+       ;; Load the project in DIR, or make one.
+       (ede-load-project-file dir)
+
+       ;; Check if we loaded anything on the previous line.
+       (if (ede-current-project dir)
+
+           ;; We successfully opened an existing project.  Some open
+           ;; buffers may also be referring to this project.
+           ;; Resetting all the buffers will get them to also point
+           ;; at this new open project.
+           (ede-reset-all-buffers)
+
+         ;; ELSE
+         ;; There was no project, so switch to `ede-new' which is how
+         ;; a user can select a new kind of project to create.
+         (let ((default-directory (expand-file-name dir)))
+           (call-interactively 'ede-new))))
+
+    ;; If the proposed directory isn't safe, then say so.
+    (error "%s is not an allowed project directory in `ede-project-directories'"
+          dir)))
+
+(defun ede-check-project-directory (dir)
+  "Check if DIR should be in `ede-project-directories'.
+If it is not, try asking the user if it should be added; if so,
+add it and save `ede-project-directories' via Customize.
+Return nil iff DIR should not be in `ede-project-directories'."
+  (setq dir (directory-file-name (expand-file-name dir))) ; strip trailing /
+  (or (eq ede-project-directories t)
+      (and (functionp ede-project-directories)
+          (funcall ede-project-directories dir))
+      ;; If `ede-project-directories' is a list, maybe add it.
+      (when (listp ede-project-directories)
+       (or (member dir ede-project-directories)
+           (when (y-or-n-p (format "`%s' is not listed in `ede-project-directories'.
+Add it to the list of allowed project directories? "
+                                   dir))
+             (push dir ede-project-directories)
+             ;; If possible, save `ede-project-directories'.
+             (if (or custom-file user-init-file)
+                 (let ((coding-system-for-read nil))
+                   (customize-save-variable
+                    'ede-project-directories
+                    ede-project-directories)))
+             t)))))
 
 (defun ede-new (type &optional name)
-  "Create a new project starting of project type TYPE.
+  "Create a new project starting from project type TYPE.
 Optional argument NAME is the name to give this project."
   (interactive
    (list (completing-read "Project Type: "
@@ -572,7 +727,7 @@ Optional argument NAME is the name to give this project."
                           'name
                           (let* ((l ede-project-class-files)
                                  (cp (ede-current-project))
-                                 (cs (when cp (object-class cp)))
+                                 (cs (when cp (eieio-object-class cp)))
                                  (r nil))
                             (while l
                               (if cs
@@ -596,6 +751,11 @@ Optional argument NAME is the name to give this project."
     (error "Cannot create project in non-existent directory %s" default-directory))
   (when (not (file-writable-p default-directory))
     (error "No write permissions for %s" default-directory))
+  (unless (ede-check-project-directory default-directory)
+    (error "%s is not an allowed project directory in `ede-project-directories'"
+          default-directory))
+  ;; Make sure the project directory is loadable in the future.
+  (ede-check-project-directory default-directory)
   ;; Create the project
   (let* ((obj (object-assoc type 'name ede-project-class-files))
         (nobj (let ((f (oref obj file))
@@ -619,7 +779,7 @@ Optional argument NAME is the name to give this project."
                                :targets nil)))
         (inits (oref obj initializers)))
     ;; Force the name to match for new objects.
-    (object-set-name-string nobj (oref nobj :name))
+    (eieio-object-set-name-string nobj (oref nobj :name))
     ;; Handle init args.
     (while inits
       (eieio-oset nobj (car inits) (car (cdr inits)))
@@ -629,6 +789,10 @@ Optional argument NAME is the name to give this project."
        (ede-add-subproject pp nobj)
        (ede-commit-project pp)))
     (ede-commit-project nobj))
+  ;; Once the project is created, load it again.  This used to happen
+  ;; lazily, but with project loading occurring less often and with
+  ;; security in mind, this is now the safe time to reload.
+  (ede-load-project-file default-directory)
   ;; Have the menu appear
   (setq ede-minor-mode t)
   ;; Allert the user
@@ -640,7 +804,7 @@ Optional argument NAME is the name to give this project."
 
 (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."
+ARGS are additional arguments to pass to method SYM."
   (if (not ede-object)
       (error "Cannot invoke %s for %s" (symbol-name sym)
             (buffer-name)))
@@ -651,11 +815,16 @@ ARGS are additional arguments to pass to method sym."
 (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)
-    ))
+  (if (not (ede-directory-get-open-project default-directory))
+      ;; This directory isn't open.  Can't rescan.
+      (error "Attempt to rescan a project that isn't open")
+
+    ;; Continue
+    (let ((toppath (ede-toplevel-project default-directory))
+         (ede-deep-rescan t))
+
+      (project-rescan (ede-load-project-file toppath))
+      (ede-reset-all-buffers))))
 
 (defun ede-new-target (&rest args)
   "Create a new target specific to this type of project file.
@@ -664,9 +833,11 @@ 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))
+  (when (and buffer-file-name
+            (not (file-directory-p buffer-file-name)))
+    (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."
@@ -707,11 +878,14 @@ a string \"y\" or \"n\", which answers the y/n question done interactively."
 
   (project-add-file target (buffer-file-name))
   (setq ede-object nil)
-  (setq ede-object (ede-buffer-object (current-buffer)))
+
+  ;; Setup buffer local variables.
+  (ede-initialize-state-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)))
+          (eieio-object-name target)))
   (ede-apply-target-options))
 
 (defun ede-remove-file (&optional force)
@@ -805,58 +979,58 @@ Argument PROMPT is the prompt to use when querying the user for a target."
 (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)))
+  (error "add-file not supported by %s" (eieio-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)))
+  (error "remove-file not supported by %s" (eieio-object-name ot)))
 
 (defmethod project-edit-file-target ((ot ede-target))
-  "Edit the target OT associated w/ this file."
+  "Edit the target OT associated with 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)))
+  (error "new-target not supported by %s" (eieio-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)))
+  (error "New-target-custom not supported by %s" (eieio-object-name proj)))
 
 (defmethod project-delete-target ((ot ede-target))
   "Delete the current target OT from its parent project."
-  (error "add-file not supported by %s" (object-name ot)))
+  (error "add-file not supported by %s" (eieio-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)))
+  (error "compile-project not supported by %s" (eieio-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)))
+  (error "compile-target not supported by %s" (eieio-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)))
+  (error "debug-target not supported by %s" (eieio-object-name obj)))
 
 (defmethod project-run-target ((obj ede-target))
   "Run the current project target OBJ."
-  (error "run-target not supported by %s" (object-name obj)))
+  (error "run-target not supported by %s" (eieio-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)))
+  (error "Make-dist not supported by %s" (eieio-object-name this)))
 
 (defmethod project-dist-files ((this ede-project))
   "Return a list of files that constitute a distribution of THIS project."
-  (error "Dist-files is not supported by %s" (object-name this)))
+  (error "Dist-files is not supported by %s" (eieio-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)))
+  "Rescan the EDE project THIS."
+  (error "Rescanning a project is not supported by %s" (eieio-object-name this)))
 
 (defun ede-ecb-project-paths ()
   "Return a list of all paths for all active EDE projects.
@@ -877,7 +1051,7 @@ 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"))
+    (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)
@@ -891,7 +1065,7 @@ Optional ROOTRETURN will return the root project for DIR."
   ;; Do the load
   ;;(message "EDE LOAD : %S" file)
   (let* ((file dir)
-        (path (expand-file-name (file-name-directory file)))
+        (path (file-name-as-directory (expand-file-name dir)))
         (pfc (ede-directory-project-p path))
         (toppath nil)
         (o nil))
@@ -907,7 +1081,7 @@ Optional ROOTRETURN will return the root project for DIR."
        ;; recomment as we go
        ;;nil
        ))
-     ;; Do nothing if we are buiding an EDE project already
+     ;; Do nothing if we are building an EDE project already.
      (ede-constructing
       nil)
      ;; Load in the project in question.
@@ -920,13 +1094,11 @@ Optional ROOTRETURN will return the root project for DIR."
       ;; 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 pfc))
-           (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)))
+
+      ;; If not open yet, load it.
+      (unless o
+       (let ((ede-constructing pfc))
+         (setq o (ede-auto-load-project pfc toppath))))
 
       ;; Return the found root project.
       (when rootreturn (set rootreturn o))
@@ -980,33 +1152,20 @@ Optional argument OBJ is an object to find the parent of."
             (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))))))))))
+            (ede-directory-get-open-project 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)
-      )
+  ;; If it matches the current directory, do we have a pre-existing project?
+  (let ((proj (when (and (or (not dir) (string= dir default-directory))
+                       ede-object-project)
+               ede-object-project)))
     ;; No current project.
-    (when (not ans)
+    (if proj
+       proj
       (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))
+       (ede-directory-get-open-project ldir)))))
 
 (defun ede-buffer-object (&optional buffer projsym)
   "Return the target object for BUFFER.
@@ -1059,26 +1218,35 @@ If TARGET belongs to a subproject, return that project file."
   "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))
+  (or ede-object-project
+      ;; If not cached, derive it from the current directory of the target.
+      (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)))
 
 (defmethod ede-find-target ((proj ede-project) buffer)
   "Fetch the target in PROJ belonging to BUFFER or nil."
   (with-current-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)))))
+
+    ;; We can do a short-ut if ede-object local variable is set.
+    (if ede-object
+       ;; If the buffer is already loaded with good EDE stuff, make sure the
+       ;; saved project is the project we're looking for.
+       (when (and ede-object-project (eq proj ede-object-project)) ede-object)
+
+      ;; If the variable wasn't set, then we are probably initializing the buffer.
+      ;; In that case, search the file system.
+      (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.
@@ -1106,8 +1274,8 @@ This includes buffers controlled by a specific target of PROJECT."
        (pl nil))
     (while bl
       (with-current-buffer (car bl)
-       (if (ede-buffer-belongs-to-project-p)
-           (setq pl (cons (car bl) pl))))
+       (when (and ede-object (ede-find-target project (car bl)))
+         (setq pl (cons (car bl) pl))))
       (setq bl (cdr bl)))
     pl))
 
@@ -1155,7 +1323,7 @@ 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.
+  "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
@@ -1177,35 +1345,48 @@ See also `ede-map-subprojects'."
 Return the first non-nil value returned by PROC."
   (eval (cons 'or (ede-map-targets this proc))))
 
-;;; VC Handling
-;;
-(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))))
-
 \f
 ;;; Some language specific methods.
 ;;
 ;; These items are needed by ede-cpp-root to add better support for
 ;; configuring items for Semantic.
+
+;; Generic paths
+(defmethod ede-system-include-path ((this ede-project))
+  "Get the system include path used by project THIS."
+  nil)
+
+(defmethod ede-system-include-path ((this ede-target))
+  "Get the system include path used by project THIS."
+  nil)
+
+(defmethod ede-source-paths ((this ede-project) mode)
+  "Get the base to all source trees in the current project for MODE.
+For example, <root>/src for sources of c/c++, Java, etc,
+and <root>/doc for doc sources."
+  nil)
+
+;; C/C++
 (defun ede-apply-preprocessor-map ()
   "Apply preprocessor tables onto the current buffer."
-  (when (and ede-object (boundp 'semantic-lex-spp-macro-symbol-obarray))
+  ;; TODO - what if semantic-mode isn't enabled?
+  ;; what if we never want to load a C mode? Does this matter?
+  ;; Note: This require is needed for the case where EDE ends up
+  ;; in the hook order before Semantic based hooks.
+  (require 'semantic/lex-spp)
+  (when (and ede-object
+            (boundp 'semantic-lex-spp-project-macro-symbol-obarray))
     (let* ((objs ede-object)
           (map (ede-preprocessor-map (if (consp objs)
                                          (car objs)
                                        objs))))
       (when map
        ;; We can't do a require for the below symbol.
-       (setq semantic-lex-spp-macro-symbol-obarray
+       (setq semantic-lex-spp-project-macro-symbol-obarray
              (semantic-lex-make-spp-table map)))
       (when (consp objs)
        (message "Choosing preprocessor syms for project %s"
-                (object-name (car objs)))))))
+                (eieio-object-name (car objs)))))))
 
 (defmethod ede-system-include-path ((this ede-project))
   "Get the system include path used by project THIS."
@@ -1215,27 +1396,66 @@ Return the first non-nil value returned by PROC."
   "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)
 
+;; Java
+(defmethod ede-java-classpath ((this ede-project))
+  "Return the classpath for this project."
+  ;; @TODO - Can JDEE add something here?
+  nil)
+
 \f
 ;;; 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.  Optional argument PROJ
+is the project to use, instead of `ede-current-project'."
+  (interactive "sVariable: \nxExpression: ")
+  (let ((p (or proj (ede-toplevel)))
+       a)
+    ;; Make the change
+    (ede-make-project-local-variable variable p)
+    (ede-set-project-local-variable variable value p)
+    (ede-commit-local-variables p)
+
+    ;; This is a heavy hammer, but will apply variables properly
+    ;; based on stacking between the toplevel and child projects.
+    (ede-map-buffers 'ede-apply-project-local-variables)
+
+    value))
+
+(defun ede-apply-project-local-variables (&optional buffer)
+  "Apply project local variables to the current buffer."
+  (with-current-buffer (or buffer (current-buffer))
+    ;; Always apply toplevel variables.
+    (if (not (eq (ede-current-project) (ede-toplevel)))
+       (ede-set-project-variables (ede-toplevel)))
+    ;; Next apply more local project's variables.
+    (if (ede-current-project)
+       (ede-set-project-variables (ede-current-project)))
+    ))
+
 (defun ede-make-project-local-variable (variable &optional project)
   "Make VARIABLE project-local to PROJECT."
-  (if (not project) (setq project (ede-current-project)))
+  (if (not project) (setq project (ede-toplevel)))
   (if (assoc variable (oref project local-variables))
       nil
     (oset project local-variables (cons (list variable)
-                                       (oref project local-variables)))
-    (dolist (b (ede-project-buffers project))
-      (with-current-buffer b
-        (make-local-variable variable)))))
+                                       (oref project local-variables)))))
+
+(defun ede-set-project-local-variable (variable value &optional project)
+  "Set VARIABLE to VALUE for PROJECT.
+If PROJ isn't specified, use the current project.
+This function only assigns the value within the project structure.
+It does not apply the value to buffers."
+  (if (not project) (setq project (ede-toplevel)))
+  (let ((va (assoc variable (oref project local-variables))))
+    (unless va
+      (error "Cannot set project variable until it is added with `ede-make-project-local-variable'"))
+    (setcdr va value)))
 
 (defmethod ede-set-project-variables ((project ede-project) &optional buffer)
   "Set variables local to PROJECT in BUFFER."
@@ -1243,25 +1463,8 @@ Return the first non-nil value returned by PROC."
   (with-current-buffer buffer
     (dolist (v (oref project local-variables))
       (make-local-variable (car v))
-      ;; set its value here?
       (set (car v) (cdr v)))))
 
-(defun ede-set (variable value &optional proj)
-  "Set the project local VARIABLE to VALUE.
-If VARIABLE is not project local, just use set.  Optional argument PROJ
-is the project to use, instead of `ede-current-project'."
-  (let ((p (or proj (ede-current-project)))
-       a)
-    (if (and p (setq a (assoc variable (oref p local-variables))))
-       (progn
-         (setcdr a value)
-         (dolist (b (ede-project-buffers p))
-            (with-current-buffer b
-              (set variable value))))
-      (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)