CEDET is a collection of elisp packages for Emacs designed to turn it into more of an IDE. While it does have the ability to better bridge the gap between editor and IDE, it can be an enormous pain in the ass to figure out; it is “documented” in a rather haphazard way, and resembles an API more than a UI, and as anyone who had to work with uncommented code knows, this is not exactly a recipe for fun.
There’s an article called “A Gentle Introduction To CEDET”, which is pretty good and helped me out in understanding the cacophony of features that is CEDET, but it certainly wasn’t enough. I also spent time reading the manuals for the various sections and had to play with the features before I finally began to comprehend how to get CEDET to do the things I installed it for, and the main take away from this experience was that CEDET has horrible usability, but it’s the best tool we have for enriching Emacs with IDE features, so someone better write an article that addresses CEDET from a functional point of view (i.e. I want to do X) rather than the “understanding the internals” point of view that too often GNU projects take. Today, I’m going to cover EDE, CEDET’s project management module.
Setting up project management is the very first thing anyone should do to use CEDET, because many of CEDET’s tools use the project definitions to get necessary information. Project management is handled by the EDE component, and supports the following features:
- Manage makefiles: add/remove targets, add/remove files to targets.
- Project configurations (i.e. debug build, release build)
- Create distribution file
- Initiate building, debugging, or running of the project.
- Viewing and updating project metadata (this includes anything from giving the linker special options to choosing a compiler to updating the project’s version number). This feature is meant for getting down and dirty with EDE, but there’s a GUI for this, so you don’t have to mess around with elisp. Note: this section is described near the end of the post.
- Searching for files in a project
- Viewing project tree organized by targets
- Managing the project’s web site. You can define a “web page file”, a host address, an FTP server address, and a mailing list address for your project. EDE includes the feature to update the project’s website and upload the distribution.
To turn EDE on, place the following code into your .emacs file:
When EDE mode is on, and you visit a project, you will see a “Project” option appear in the menubar. Many of the commands I’m about to describe can be accessed via that dropdown menu.
The EDE world view
There is one important thing to understand about EDE, and that is the way in which it looks at your projects.
In EDE’s view, a “project” is files within a folder + their makefile. If you have multiple folders in your codebase, then each one of those folders needs to be defined as a “project” as well, and these “projects” are considered to be subprojects of the top level project.
It’s worth mentioning that EDE is aware of things that happen down the folder hierarchy. You can be at the top folder, initiate a file search, and it will search the entire hierarchy. When you add a subproject, the makefile for the top project is automatically updated to build the subproject as well.
Supported EDE use cases
The question you need to ask yourself when you decide to define a project is which one of the following scenarios fits you:
- I’m starting my project from scratch, and want EDE to manage it for me.
This mode of operations is the most feature complete because as you write your code you set everything up the way EDE wants it to be. This option lets you create either Makefile projects or Automake projects.
- I have existing source code that EDE knows how to work with.
This mode of operation is good for Automake projects, and 2 specially supported projects: Emacs and the Linux kernel. In these situations you need to just go to the source code and EDE will identify everything automatically. This mode of operation is less feature complete, in part because it assumes that since you have existing makefiles, you probably want to keep managing them yourself.
- I have existing source code that EDE doesn’t know how to work with.
In this mode of operations none of the project management features will work, but you can still use EDE to define your code as a project. The reason for doing this is so that some limited amount of metadata will be available for other tools in CEDET. This mode of operation will require you to write elisp code, but you can have a minimal setup with very few lines of elisp. I’ll explain how to do this easily further in the post (there’s a template). Since C/C++ projects with existing Makefiles are common, EDE has a special “project mode” for them.
Note: EDE now supports Android and Arduino projects too. These features appear to be relatively new, and I will not cover them in this tutorial.
With that in mind, let’s go over the 3 use cases…
EDE with new projects
The EDE documentation has a quick start section that is worth looking at for a short example of how to work with EDE for new projects. The general gist is as follows:
Suppose we have a folder hierarchy as depicted in the picture above: a folder with c files and a header, and two subfolders, also with c files and a header in each (currently there are no makefiles or Project.ede files). You need to do the following:
- Open a file in the top level folder, and type M-x ede-new. This will ask you for a project name and type: make or automake. This creates a Project.ede file in your directory.
- Type M-x ede-new-target or C-c . t, and it will ask you for a name, target type (see below), and if you want to add the current file to that target.
- Repeat step 2 as needed to create all the targets you want.
- Open a file in the current directory, and type M-x ede-add-file or C-c . a and it will ask you which target you want to add this file to.
- Repeat step 4 as needed to add all the files to the necessary targets.
- Repeat steps 1-5 for each subfolder, and then their subfolders, and so on.
Supported targets for ede-new-target:
The command ede-new-target requires you to specify what kind of target it is. The following is a list of possible targets and what they are meant for (thanks to Alex Ott from stackoverflow.com for the list).
archive– object code archive/static library
auxiliary– handles auxiliary files in project – README, *.txt, etc. When I played around with this type of project the header files I added to a “program” target ended up here as well. It looks like this is for files that don’t get compiled.
emacs lisp– compiles Emacs Lisp code
emacs lisp autoloads– target to create file with Elisp autoloads
info– handles texinfo files
miscellaneous– target that allows user to compile other parts of project, using user-provided
Makefile(one that is not generated by EDE)
program– builds executable program
scheme– target to handle Scheme (guile) source code
semantic grammar– target to correctly build Semantic’s grammar files, both for Wisent & Bovine. This target is defined in Semantic, not in EDE
sharedobject– target to build shared libraries
The following is the complete list of features that EDE supports. When working in this manner, they should all work or be supported:
Create target: Creates a target for your makefile, and possibly adds the current file to it.
Invocation: M-x ede-new-target or C-c . t
Remove target: Removes a target from a makefile.
Invocation: M-x ede-delete-target
Add file to target: adds the current file to a target of your choice.
Invocation: M-x ede-add-file or C-c . a
Remove file from target: removes the current file from a target of your choice.
Invocation: M-x ede-remove-file or C-c . d
Building: build the current project or target. You can access more target building options from the menus.
Invocation: C-c . c for current target, C-c . C for entire project.
Debug target: Run a debug session on current target.
Invocation: M-x ede-debug-target or C-c . D
Run target: Run the current target
Invocation: M-x ede-run-target or C-c . R
Create distribution file: Creates a tar.gz file with your code in it.
Invocation: M-x ede-make-dist
Locate files: search for a file in your project.
Invocation: M-x ede-find file or C-c . f
Speedbar: Open a project hierarchy navigation side buffer that shows your files grouped by targets.
Invocation: M-x ede-speedbar or C-c . s
Make distribution: This command will create a tar.gz archive of your project.
Invocation: M-x ede-make-dist
Upload distribution: This command will upload the distribution file to the FTP server you define. Using this feature will require you to define the FTP server’s address via the customization feature, which is covered later.
Invocation: M-x ede-upload-distribution
A personal note:
Having played around with EDE for a bit my impression of it is that it’s cute, but not overly useful. While the initial creation of targets is kinda nice, the moment you decide that you want to do something more you’ll quickly find EDE getting in your way, because it’s nothing more than a layer of (bad) abstraction over the Make system. I’m saying “bad” because an abstraction should mean that you create a new, simpler “language” that the user can use without knowing what is underneath it, but in the case of EDE it doesn’t hide any of Make’s complexities from the user, but rather gives you a different way to specify the things that you want to do, and probably already know how to do by hand anyway (not to mention that unless you know how makefiles work, you’re not going to be able to make any sense out of the customization buffers of your targets).
For example, suppose that you need to compile your code using special flags (-Wall) and link a bunch of libraries (-lm). In EDE this means opening the target’s customization buffer and searching for the option to specify libraries you want to link, and flags you want to pass to the compiler. If you already know what these options mean, you already know how to type them in the Makefile and where, and it would be much more natural to just do that instead of figuring out what’s the “EDE way” of doing the same thing.
What makes this even worse is that the way you specify some options is by overwriting the default values of Makefile variables that EDE generates, and how do you know what these variables are and how they are used? By looking at the Makefile of course! Yes, some of them are pretty standard and once you learn them you don’t need to check the Makefile anymore, but the point is that if you need to reference the internal structure of the Makefile then what kind of an abstraction is this? How is this helping us not mess around with Makefiles (which is presumably the reason we want EDE to manage them for us)??
And if this wasn’t enough, there’s also the myriad of Make features that EDE doesn’t support, which is a headache in its own right if you want to do anything remotely non-trivial. This includes basic stuff like initializing a variable using ?= for example. The EDE workaround for this is to let you define files to “include” into the Makefile, which basically means “write all of that stuff in your own Makefile, and tell EDE to include it in the Makefile that it generates”. Again, if the way to do these things is by hand, it’s going to be much more natural to just write it in the main Makefile, and not write a second Makefile and include it in the first.
In conclusion, this method of working is sort of kinda OK if you want to do some simple stuff, but I can’t imagine any serious project with a degree of non-trivial complexity using it, and quite frankly you’re better off doing the simple stuff by hand as well, because EDE is just going to get in your way, and its extra features like making an archive of your code and uploading it to an FTP server are far from being “killer features” that will make the hassle worth it.
EDE with existing code that EDE doesn’t recognize
For any not-officially-supported scenarios EDE has the notion of a “simple project”, which is basically a do it yourself option. ede-simple-project is an EIEIO object that is designed to be subclassed, and you are free to implement all the necessary elisp functions by hand to support whatever situation you have in your project. If you know how to do that, you don’t really need mu help.
Simple projects are defined by writing elisp code in your .emacs file, and are pretty much featureless. None of the major commands like building and creating targets will work, but the more trivial ones like “Edit Project Homepage” or “Browse Project URL” will, as long as you manually set the appropriate variables in your elisp code (since there’s no Project.ede where settings can be persistent, it is not possible to set this via the customization features of EDE). However, like I said at the start of this post, you will still want to use a simple project even if you don’t plan to extend it, just so that other parts of CEDET will have access to the limited metadata it does have. Otherwise, you may have to configure all the other CEDET tools by hand.
I will however show you how to setup a simple C/C++ project, which doesen’t take much knowledge to do. Its EIEIO object ede-cpp-root-project is special because it’s aware of some typical C/C++ idiosyncrasies like the abundance of .h files. A basic configuration can be created like this:
(ede-cpp-root-project "project_name" :file "/path/to/some/top/level/file" :include-path '( "/include" "include/configs" ) :system-include-path '( "/usr/include/c++/3.2.2/" ) :spp-table '( ("MOOSE" . "") ("CONST" . "const") ) )
Hopefully most of the stuff you see in this code sample is self explanatory even if you don’t know elisp. Essentially what this code does is give the project a name, tell CEDET where the the project root is by pointing it to a file in that root directory, tells it where the include files are (an entry starting with a “/” is relative to the project root, while an entry that doesn’t start with “/” is interpreted as relative to the current buffer), tells it where the system include files are, and then there’s that spp-table variable.
spp-table is where you can list a bunch of C preprocessor defines that are critical for the various #ifdefs in your code. CEDET can theoretically find them while parsing the code in your project, but placing them here explicitly makes sure that they are indeed found when needed.
You can see a full documentation of ede-cpp-root-project in the EDE manual under the Project/ede-cpp-root-project entry. Examining the inheritance tree can give you the full list of elisp “slots” (variables) you can configure in the above template. The full list is this (items you probably not need to set are grayed out):
- name: String
Default Value: “Untitled”
Used for distribution files. No reason to set this since Make Distribution is not supported in simple projects.
- version: String
Default Value: “1.0″
Used for distribution files. No reason to set this since Make Distribution is not supported in simple projects.
- directory: String
Directory this project is associated with. Not sure what its use is considering that this can be inferred with the help of the file variable.
- file: String
A name of a file in a top level directory of the project.
- targets: List
List of top level targets in this project. This is used by EDE when it generates Makefiles, and I’m not sure of whether you have a reason to set this or not in a “simple project”, although I suspect that it might help other tools find some stuff better if you do (I myself do not bother to set it).
- tool-cache: List
List of tool cache configurations in this project. This allows any tool to create, manage, and persist project-specific settings. If I’m not mistaken this section is for plugging in custom tools (notice that the “tools” section of customize-project is always empt).
- web-site-url: String
URL to this projects web site. This is a URL to be sent to a web site for documentation.
- web-site-directory: String
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: String
A file which contains the home page for this project. This file can be relative to slot `web-site-directory’. Same guidelines as above.
- ftp-site: String
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: String
FTP Site to upload new distributions to. Same guidelines as above. If this slot is `nil’, then use `ftp-site’ instead.
- configurations: List
Default Value: (“debug” “release”)
List of available configuration types. Individual target/project types can form associations between a configuration, and target specific elements such as build variables. Not useful to you unless you plan to write your own elisp functions that use this.
- configuration-default: String
Default Value: “debug”
The default configuration. Not useful to you unless you plan to write your own elisp functions that use this.
Default Value: `nil’
This variable is for setting Emacs variables that you want be set only for this specific project.
- include-path: List
Default Value: `(quote (“/include” “../include/”))’
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: List
Default Value: `nil’
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: List
Default Value: `nil’
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. I myself do not bother setting this.
- spp-files: List
Default Value: `nil’
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: String
Default Value: “\\.\\(h\\(h\\|xx\\|pp\\|\\+\\+\\)?\\|H\\)$\\|\\<\\w+$”
Regexp used to identify C/C++ header files. If you need to set this then wat.
- locate-fcn: function (or null)
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 with existing code that EDE recognizes
As mentioned before, there are a few use cases for this option: Automake projects, Linux project, and the Emacs project. It is worth noting that in EDE’s eyes, existing Automake project and Linux/Emacs projects are actually two different things. Linux/Emacs projects are essentially “simple projects” that are preconfigured and prepopulated with symbols.
I don’t have much to say about this option. It’s going to be somewhere between “simple project” and a fully featured project type.
Advanced: Low level customization of your project
Most of EDE’s project metadata isn’t accessible from the high level commands described above, so in order to customize it you either have to edit the project definition by hand, or preferably use the customization feature. This feature worksin two possible situations:
- Your EDE setup uses a Project.ede file (use case number 1).
If your setup uses a Project.ede file, the customization feature will take you to a special buffer similar to Emacs’ “customize” buffer.
- Your EDE setup does not use a Project.ede file (use cases 2 and 3).
In this situation EDE is either using Makefiles or some other file to learn about the project’s setup, and the customization feature will take you to these files themselves. For example, if you are in the Linux project, and type C-c . e, it will take you to scripts/ver_linux.
The customization buffers can be accessed via the Project menu, and look something like this:
As you can see, the different options are documented. Some of the documentation in there is helpful, other not so much. Since this post is already long enough, I’ll focus on describing what I personally feel is the non-obvious stuff.
- For Automatic-Dependencies, Include-File, and Inference-Rules you can view this post.
- Variables: literally insert variables into the makefile.
- Configuration-Variables: insert variables that will only be used when compiling with the appropriate project “configuration”.
- Project Local Variables: set Emacs settings that will be relevant only for this project.
- Auxiliary Files under “Local Targets”: when I added header files to my project, they ended up here, which is not what I would’ve expected given the description in the documentation.
- Linker and Compile options: I didn’t set any, and it appears that this causes EDE to default to GNU tools like GCC.
- Additional Rules is for inserting hand written rules.
- LdLibs: despite what the documentation says, this is perfectly supported in Make projects as well.
Confused yet? I don’t blame you.
If you want my opinion: don’t bother working with EDE directly. It’s not really worth it. Just write your own Makefiles, and use EDE’s ede-simple-project or ede-cpp-root-project to define where your includes are, and if you want to- what you web site URL and FTP address is for all those cutesy features. If you don’t want to pollute your .emacs file with project specific definitions, you can do the following:
Place this code into you .emacs file:
;; Create a Project.ede equivalent for ede-simple-project ;; by telling Emacs to load Project.el files (defun check-for-Project-el () (if (file-exists-p "Project.el") (load-file "Project.el") ) ) (add-hook 'find-file-hook 'check-for-Project-el)
This function makes Emacs check for the existance of a Project.el file whenever it opens some file, and loads its elisp code. Now, place a Project.el in your project folder, and write all the configuration there.
So that’s it for now. Next time I’m going to cover Semantic.