BeIDE Plugin API

============================
Purpose of the Plugin API
============================

BeIDE can utilize any command line tool during the normal build process.  This
gives developers great flexibility in controlling builds.  The intention is to 
separate tool-specific code from the project manager and build capabilities
of BeIDE.  

In order for a tool to be supported by BeIDE a plugin that contains code for
MPlugInPreferencesView and a MPlugInBuilder objects must be available.  The
MPlugInPreferencesView will be shown in the Settings window and settings
created by this view will be stored in the project.  At build time the
MPlugInBuilder object will be called and asked to generate an argv list
for each source file targeted to the tool supported by that builder object.  The
targets preferences panel provides a mechanism for the project manager to 
associate source files with specific tools and to specify the stage of make
during which a particular source file is executed.

BeIDE supports command line tools that are not aware of the IDE as well as 
tools that are IDE aware.  IDE aware tools have access to a port-based interface
to the IDE that allows greater access to the information stored in a project
than is possible through an argv interface.  Non-IDE-aware tools are simply
launched as if from the command line and won't require any rewrite in order
to function as part of the build process.


============================
Parts of a Plugin
[Please note for Nasdaq release:  The Plugin API has changed.  The old names were
MakePlugInView and MakePlugInBuilder.  In addition to this name change, many methods
of MPlugInBuilder take an additional MProject parameter.]
============================

Plugins are straightforward add-ons.  They must export two functions with
the prototypes as shown below:

extern "C" status_t MakeAddOnView(int32 inIndex, BRect inRect, MPlugInPrefsView*& outView);
extern "C" status_t MakeAddOnBuilder(int32 inIndex, MPlugInBuilder*& outBuilder);

Linker plugins (of which only one is allowed) must also export:
extern "C" status_t MakeAddOnLinker(MPlugInLinker*& outLinker);

MakeAddOnView and MakeAddOnBuilder are called repeatedly until they return a value
other than B_OK.  inIndex will start at zero and be incremented for each
call.  inRect is the frame rect for the preferences view in coordinates of the
Settings window.  outRec and outBuilder are references to object pointers for
the view and builder objects that are returned by the add-on..

When called, the MakeAddOnView and MakeAddOnBuilder functions should generate the 
view and builder objects that match inIndex, and should place those object pointers 
in the respective outView or outBuilder parameters, and should return B_OK.
Once inIndex is greater than the number of views (or builders) that the add-on 
generates it should return B_ERROR.

A given add-on can generate multiple views and builders.  A view needn't have a
matching builder object and in this case the builder variable in the PrefsPlugInRec
must be nil.  In the case where a given tool requires multiple prefs views
only a single builder object should be generated for the tool.

In the simplest and most common case the add-on will generate a single view and matching
builder.  The view that is returned must be a subclass of MPlugInPreferencesView and
the builder must be a subclass of MPlugInBuilder.  Header files describing those classes
should accompany this document.  Both classes are pure virtual with a number of
functions that must be overridden in any subclasses.  Some of the functions won't 
apply for all subclasses but they must all be overridden.  See the function descriptions
below for more information on what each function does.


============================
What happens and When?
============================

Plugins are loaded when BeIDE launches.  The MakeAddOn function will be called shortly
after launch and before any windows appear.  The Settings window is persistent, being
created at app launch and existing until app quit.  MPlugInPreferencesViews will be 
destructed when the Settings window is destructed at app quit time.  MPlugInBuilders will
be destructed shortly before the views are destructed, also at app quit time.  Ownership
of the views and builders passes to the IDE and they shouldn't be destructed by any plugin
code.

MPlugInPreferencesViews will be shown and hidded under user control.

MPlugInBuilders will be called to validate settings data at various times and will be called
to generate argv lists for each source file.

The build process consists of four stages, precompile, compile, link, and postlink,
which execute in that order.  Files for each stage are executed from top to bottom
in the project window and all files for one stage will execute before the next 
stage is begun.

Source files are targeted to a particular stage by their
file type and file extension, as set in the Targets preferences panel.  Source files
are also associated with a MPlugInBuilder object based on the toolname specified
by that object and the toolname specified in the target record in the Targets
preferences panel.  The relationship is Source File->Target record->MPluginBuilder(s).

The precompile and compile stages are rather similar.  The link stage is
calls the MPlugInLinker to do the work.

After a successful link the postlink stage is begun.  First all resources in
source files are copied to the executable.  Files containing resources are
indicated by the hasResources flag set in the Targets preferences panel.  The
make stage and toolname are ignored for the resource copy step.  Next the 
access permissions are set for the executable with a call to chmod() and the
file type and creator signature are set.  Finally, those source files 
targeted to the postlink stage are executed.


============================
Function Reference
============================

============================
MPlugInPreferencesView
============================

-- MPlugInPreferencesView::MPlugInPreferencesView

The constructor does nothing and is there to allow subclasses to call the BView
constructor.

-- GetTitle

virtual void GetTitle(char* outName);

Returns the title of the prefs view.  This title is used for the text in the
scrolling list of prefs panels in the Settings window.  The title may also indicate
the section of the scrolling list in which the prefs panel should be placed.
This is done with a title like:

"Project/Target"

where 'Project' is the section and 'Target' is the name of the prefs panel.
If no section is specified the prefs panel will go into a section at the end
titled 'Other'.  There is no need to specify 'Other' in your panel titles since
they'll end up there anyway.  You may not additional sections to the scrolling list.

-- Targets

virtual TargetT Targets();

Each prefs panel can be targeted to one or more parts of the project manager.
These targets are shown in the Apply To popup in the Settings window.  In almost
all cases the targets for plugin prefs panels will be new projects and the current
project.  The Targets function will almost always look like: 

TargetT
MSamplePlugInView::Targets()
{
	return (kMWDefaults | kCurrentProject);
}

Note that 'target' in this sense is not related to the Targets prefs panel.

-- GetData

virtual void GetData(BMessage& inOutMessage);

Preferences data is passed to and from preferences views and builders in BMessages.
BMessages are suited for this because they are general container objects.  It
is simple to add arbitrary data to a BMessage and later retrieve that data
by name and type.

Each builder object has associated with it a type.  At build time all data of that
type will be passed to the builder, which will be able to distinguish
between differenct preferences data by the unique names.  Each preferencesview object 
can edit this sort of data (usually one struct per preferencesview).

GetData is called with an empty BMessage.  The preferencesview should add to that
message the appropriate data.  For example:

const char*	kSampleMessageName = "SampleData";
const ulong kSampleMessageType = 'SamD';

void
MSamplePlugInView::GetData(BMessage& inOutMessage)
{
	inOutMessage.AddData(kSampleMessageName, kSampleMessageType, &fNewSettings, sizeof(fNewSettings));
}

GetData will be called at several times: when the Save button is clicked, and when the target
of the prefs view changes, which happens when the ApplyTo popup is changed.  The Settings window
uses the name and type of the data in the BMessage to obtain the current settings for this
view from the target.

-- SetData

virtual void SetData(BMessage& inOutMessage);

SetData will be called when the current settings of a target need to be displayed in the view.
The view should look in the BMessage for data of the type and name that the preferencesview
edits and should make a copy of the data and update the view reflecting the new prefs data.

Please note that a view might need to know the MProject it is working with.  On all calls
to SetData, there is an additional item in the BMessage that can be used to discover the
MProject for the view.  To get the MProject, the SetData code could do the following:

void
MSamplePlugInView::SetData(BMessage& inMessage)
{
	if (inMessage.HasData(kSampleMessageName, kSampleMessageType))
	{
		long			length;
		inMessage.FindData(kSampleMessageName, kSampleMessageType, (const void**) &prefs, &length);

		fNewSettings = *prefs;
		fOldSettings = fNewSettings;

		// I need to know the MProject this view is targeting...
		inOutMessage.FindPointer("MProject", (void **) &fProject)
		
		UpdateValues();
	}
}


-- GetPointers

virtual void	GetPointers(void*& outOld,
							void*& outNew,
							long& outLength);

In most cases the prefs data generated by pluginviews and utilized by builders will
be represented by a simple struct.  In those cases the Settings window can handle
a number of functions for the preferences view including saving, reverting, and
determining if the prefs data have changed.  If the prefs data can be represented
as a simple struct GetPointers should set the values of the outOld, outNew, and 
outLength parameters to point to the oldvalues struct, newvalues struct, and
the length of these structs.

The strategy to be used for maintaining these structs is to have two copies
of the struct as member variables of the preferencesview.

struct SampleData {
	long		version;
	long		fieldOne;
	bool		fieldTwo;
};

SampleData		fNewSettings;
SampleData		fOldSettings;

The fOldSettings struct holds the initial settings obtained in the SetData call.
The fNewSettings struct holds values that result when the user manupulates subviews
in the preferencesview.  The fNewSettings struct should be updated after every
change that the user makes.  The Settings window can then do a Revert simply by 
copying the fOldSettings struct to the fNewSettings struct and calling UpdateValues.
Other actions can be performed in a similar fashion.


-- DoSave

virtual void	DoSave();

This function will be called only if GetPointers doesn't return valid values.
It will be called after the save button is clicked.  The preferencesview should
copy its data structures that hold the new prefs data to the data structures that
hold the old prefs data. 

-- DoRevert

virtual void	DoRevert();

This function will be called only if GetPointers doesn't return valid values.
It will be called after the Revert button is clicked.  The preferencesview should
copy its data structures that hold the old prefs data to the data structures that
hold the new prefs data. 

-- DoRevert

virtual void	DoFactorySettings();

This function will be called when the factory settings button is clicked.  The 
preferencesview should replace the new settings values with appropriate
defaults.  The old settings values shouldn't be changed.

-- UpdateValues

virtual void	UpdateValues();

At various times the Settings window may modify the new settings struct.  If it
does it will call this function.  The preferencesview should read all the values
from the new settings struct and set the values of all the subviews accordingly.

-- ValueChanged

virtual void	ValueChanged();
????

-- FilterKeyDown

virtual	bool FilterKeyDown(ulong aKey)

This function is called from the FilterKeyDown function of the Settings window.  It allows
the view to handle various keys in a special manner if desired, such as the tab key.
Return true if the key is not to be passed to the focusview.

-- ProjectRequiresUpdate

virtual	bool ProjectRequiresUpdate(UpdateType inType);

If the target of the prefsview is the current project certain changes will require
all of the files in the project to be recompiled or will require the project to
be relinked.  Return true changes in the prefsview will require the specified type
of changes in the project.


============================
MPlugInBuilder
============================

All methods that can be called during a build are called with an MProject& parameter.
This parameter is used to distinguish what project the build action applies to.  Please
notice that it is possible that two projects are being built at the same time, so
calls to the different MPlugInBuilder methods can be interwoven between multiple
projects.

If the MPlugInBuilder wants to save information for a project.  (For example, the
associated object directory or some preference information.) It should save it in a 
map like structure.  The key would be the MProject and the value would be the data 
associated with that project.  Then any method that wants to work with that data 
would first do a map lookup to find the appropriate data for that MProject.  
Under no circumstances should assumptions be made regarding the order of calls 
to the MPlugInBuilder.

The MProject parameter can safely be saved and used between the two calls of
ProjectChanged(aProject, kProjectOpened) and ProjectChanged(aProject, kProjectClosed).  
After the ProjectChanged(aProject, kProjectClosed) that MProject is no longer
valid and should no longer be used.

-- ~MPlugInBuilder

The destructor doesn't do anything but is present so that any derived class destructors
will be virtual.

-- GetToolName

virtual status_t	GetToolName(MProject* inProject,
								char* outName,
								int32 inBufferLength,
								MakeStageT inStage,
								MakeActionT inAction);

This function returns the name of the tool associated with this builder.  This toolname 
is used to associate a tool with a given source file based on the information in
the Targets prefs panel.  The tool may reside in /boot/develop/tools or in /boot/bin,
in which case the toolname can be simply the filename of the tool (e.g, "mwcc", "sh").
Alternatively the toolname may be a fullpath to the tool, in which case it may reside
anywhere in the filesystem.  In either event the toolname should be copied to the
buffer specified by outName and inBufferLength.

Under certain circumstances multiple tools can be associated with a single builder
depending on the make stage and make action. (And for this reason, the MProject
associated with the tool is also passed into the method.  Notice that inProject 
can be nil during tool setup and target handling).

-- MakeStages

virtual MakeStageT MakeStages();

Return a mask that indicates the stages of make at which this tool can act.
See PlugInPreferences.h for valid values of this mask.  For example:

MakeStageT
MSampleBuilder::MakeStages()
{
	return (kPrecompileStage | kCompileStage);
}

-- Actions

virtual MakeActionT Actions();

Return a mask that indicates the actions this tool can perform.
See PlugInPreferences.h for valid values of this mask.  For example:

MakeActionT
MSampleBuilder::Actions()
{
	return (kPrecompile | kCompile | kPostLinkExecute);
}

-- Flags

virtual PlugInFlagsT Flags();

Return certain flags regarding this builder.  Currently the only flags defined
indicate whether the tool is IDE-Aware or not.  Most tools will not be IDE-Aware.

PlugInFlagsT
MSampleBuilder::Flags()
{
	return kNotIDEAware;
}

-- MessageDataType

virtual ulong MessageDataType();

The message data type is the type of data added to the BMessage in the 
MPlugInPreferencesView::SetData function.  At build time the build system
will copy all data of this type from the project and pass it to the
BuildArgv functions so they can use it to generate their argv lists.

-- ValidateSettings

virtual bool ValidateSettings(BMessage& inOutMessage);

This function is called to give the builder the opportunity to verify that the
prefs data in the message is valid.  The builder should inspect the message for
all data that the builder will require when generating an argv list.  If any
data is missing it should be added to the message.  If any data is in an old
format or is otherwise invalid it should be replaced in the message.  Return
true if something in the message was changed.

This function will be called shortly after app launch to validate the new
project settings and shortly after any project is opened.  It will help 
this validation step if the preferences data has a version field in it.
If possible when updating to a new prefs version format the existing
values should be copied to the new struct.

bool
MSampleBuilder::ValidateSettings(BMessage& inOutMessage)
{
	SampleData	defaultPrefs = { kCurrentVersion, 1L, TRUE };
	bool		changed = false;

	if (inOutMessage.HasData(kSampleMessageName, kSampleMessageType))
	{
		long			len;
		SampleData*		prefsPtr;
		inOutMessage.FindData(kSampleMessageName, kSampleMessageType, (const void **) &prefPtr, &len);
		
		if (prefsPtr->version != kCurrentVersion || len != sizeof(SampleData))
		{
			inOutMessage.ReplaceData(kSampleMessageName, kSampleMessageType, 
				&defaultPrefs, sizeof(defaultPrefs));
			changed = true;
		}
	}
	else
	{
		inOutMessage.AddData(kSampleMessageName, kSampleMessageType, 
				&defaultPrefs, sizeof(defaultPrefs));
		changed = true;
	}
	
	return changed;
}

-- BuildPrecompileArgv

virtual status_t	BuildPrecompileArgv(MProject& inProject,
										BList& inArgv,
										MFileRec& inFileRec);

There are four stages of make and four functions for building argv lists.
These functions have similar but not identical parameter 
lists.  This function will be called as part of a Make or Bring Up To Date.
It should return B_NO_ERROR if a valid argv list was generated.

inArgv is a BList object.  It is empty on entry and holds the argv
arguments on exit.  These arguments must be strings allocated with
malloc, or more commonly with strdup.  (They will be released by
the BeIDE with free().)  The arguments will be passed to the tool 
in the order that they appear in the BList. 
Do not add a NULL entry at the end.

Here are two alternate types of methods showing how preference
data might be fetched.

virtual long	
MSampleBuilder::BuildPrecompileArgv(MProject& inProject,
									BList& inArgv,
									MFileRec& inFileRec)
{
	long error = B_ERROR;

	BMessage message;
	inProject.GetPrefs(kSampleMessageType);
	if (message.HasData(kSampleMessageName, kSampleMessageType))
	{
		long len;
		SampleData* prefsPtr;
		message.FindData(kSampleMessageName, kSampleMessageType, (const void**) &prefsPtr, &len);

		if (prefsPtr->fieldTwo)
			inArgv.AddItem(strdup("-c"));
		else
			inArgv.AddItem(strdup("-o"));
		
		inArgv.AddItem(strdup(inFileRec.path));
		
		error = B_NO_ERROR;
	}

	return error;
}

virtual long	
MAnotherSampleBuilder::BuildPrecompileArgv(MProject& inProject,
										   BList& inArgv,
										   MFileRec& inFileRec)
{
	long error = B_ERROR;

	// Project Settings cached in ProjectChanged
	SampleData* prefsPtr = this->GetCachedProjectSettingsForProject(inProject);
	if (prefsPtr->fieldTwo)
		inArgv.AddItem(strdup("-c"));
	else
		inArgv.AddItem(strdup("-o"));
	
	inArgv.AddItem(strdup(inFileRec.path));
	
	error = B_NO_ERROR;
}

-- BuildCompileArgv

virtual status_t	BuildCompileArgv(MProject& inProject,
									 BList& inArgv,
									 MakeActionT inAction,
									 MFileRec& inFileRec);

The parameters for BuildCompileArgv() are similar to those for BuildPrecompileArgv
except that there is an additional inAction parameter.  The only valid action
at the precompile stage is kPrecompile.  However, at the compile stage
a number of actions are valid (kPrecompile, kCompile, kPreprocess, kCheckSyntax, 
and kDisassemble).  This function will be called when any of the above actions
are initiated from menu items in the Project Window or source windows.  This function
will also be called for source files during the compile stage of a Make or
Bring Up to Date.

-- BuildPostLinkArgv

virtual status_t	BuildPostLinkArgv(MProject& inProject,
									  BList& inArgv,
									  MFileRec& inFileRec);

This function will be called for source files that are executed in 
the postlink stage.  The executable will already exist and its
record_ref is passed as a parameter.

-- FileIsDirty

virtual bool	FileIsDirty(MProject& inProject,
							MFileRec& inFileRec,
							MakeStageT inStage,
							MakeActionT inAction,
							time_t inModDate);

During a Make or Bring up to Date the build system only executes those
files that have been modified since the last sucessful compile or have
been expressly 'Touched', or those which have dependent files that
have been modified.  For each source file the build system will
check each of those things.  If it determines that the file hasn't
been modified it will call this function in the builder to allow
the builder to additionally determine if the file needs to be
executed.  Return true if the file needs to be executed.

inFileRec is the MFileRec of the source file.  inProject is the 
MProject being checked.  inStage is the stage of Make and inAction is the
action.  inModDate is the moddate of the source file as
returned from BStore::ModificationTime();

-- ParseMessageText

virtual status_t	ParseMessageText(MProject& inProject,
									 const char* inText,
									 BList& outList);

Non-IDE-Aware tools commonly send messages to stderr or stdout.  When
launching Non-IDE-Aware tools the build system captures these messages.
In order for them to appear in the Message window in some meaningful
way this function will be called.  It's job is to take the text
specified in inText, break it into ErrorMessage structs and add
these structs to outList.  The ErrorMessage struct is defined in 
ErrorMessage.h.  This function will not be called for IDE-AWARE tools.

The ErrorMessage struct holds a larg number of variables to precisely locate
the error so that double-clicking the error in the Message window will
open the file and select the error, even if some text in the source file
has changed.  In most cases though this information will not be
available from commandline tools and may not be relevant.  In those cases
simply set the textonly field to true and copy the text to the 
errorMessage field of the struct.

inText is the text that is captured from stderr and stdout.  It is 
additionally terminated with a null character.  Allocate ErrorMessage
structs with operator new.  For example:

	ErrorMessage*	msg = new ErrorMessage;
	
	msg->textonly = true;		// ignore most of the fields in the struct
	msg->isWarning = false;		// it's an error not a warning	
	msg->filename[0] = 0;		// no filename
	
	memcpy(msg->errorMessage, &text[beginOffset], textLen);
	
	outList.AddItem(msg);

-- CodeDataSize

virtual void	CodeDataSize(MProject&	inProject,
							 const char* inFilePath,
							 int32& outCodeSize,
							 int32& outDataSize);

This function will be called for non-IDE-Aware tools after the successful
execution of a source file.  The code size and data size values returned
will be displayed in the project window.  If code size and data size
aren't relevant for a given source file return a negative value.

-- GenerateDependencies

virtual status_t	GenerateDependencies(MProject& inProject,
										 const char* inFilePath,
										 BList& outList);

This function will be called for non-IDE-Aware tools after the successful
execution of a source file.  Populate outList with a list of entry_ref's
for each file included by inFilePath.
	
-- GetTargetFilePaths

virtual void	GetTargetFilePaths(MProject& inProject,
								   MFileRec& inFileRec,
								   BList& inOutTargetFileList);

Add the full path of the target file(s) for the source file specified
by inFileRec to the inOutTargetFileList list.  This is used to generate
the link command for linking the project.

-- ProjectChanged

virtual void	ProjectChanged(MProject& inProject,
							   ChangeT inChange);

ProjectChanged is called whenever a project is opened or closed, or when
other significant events happen in the life of the project.  See ChangeT
for a list of the events that will trigger a call to ProjectChanged.
Notice that with multi-project support, ProjectChanged can be called
with kProjectOpened multiple times (once for each open project).  This
means that if you cache information related to the project, you must
deal with multiple caches.  In each other method that might use that
project specific information, you need to retrieve the correct cache
based on the MProject parameter. The structure of ProjectChanged 
normally follows something like this:

void
MSampleBuilder::ProjectChanged(MProject& inProject, ChangeT inChange)
{
	switch (inChange)
	{
		case kProjectOpened:
			CacheProjectSettingsInMap(inProject);
			break;

		case kProjectClosed:
			RemoveProjectSettingsFromMap(inProject);
			break;

		case kPrefsChanged:
			ChangeProjectSettingsInMap(inProject);
			break;
			
		case kFilesAdded:
		case kFilesRearranged:
		case kFilesRemoved:
		case kBuildStarted:
		case kRunMenuItemChanged:
		case kLinkDone:
			OtherStuff(inProject)		
			break;
	}
}


============================
MPlugInLinker
============================

Currently not documented.
