461 lines
11 KiB
C++
461 lines
11 KiB
C++
/*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
/*
|
|
mac_xpt_linker.cpp
|
|
|
|
CodeWarrior plugin linker, links together multiple .xpt files.
|
|
|
|
by Patrick C. Beard.
|
|
*/
|
|
|
|
/* standard headers */
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <setjmp.h>
|
|
|
|
/* system headers */
|
|
#include <Files.h>
|
|
#include <Strings.h>
|
|
|
|
/* compiler headers */
|
|
#include "DropInCompilerLinker.h"
|
|
#include "CompilerMapping.h"
|
|
#include "CWPluginErrors.h"
|
|
|
|
/* project headers */
|
|
#include "mac_xpidl_panel.h"
|
|
#include "mac_console.h"
|
|
#include "mac_strings.h"
|
|
#include "FullPath.h"
|
|
#include "MoreFilesExtras.h"
|
|
|
|
/* use standard CodeWarrior debugger */
|
|
#define kDebuggerCreator 'MWDB'
|
|
|
|
/* prototypes of local functions */
|
|
static CWResult Link(CWPluginContext context);
|
|
static CWResult Disassemble(CWPluginContext context);
|
|
static CWResult GetTargetInfo(CWPluginContext context);
|
|
|
|
extern "C" {
|
|
pascal short xpt_linker(CWPluginContext context);
|
|
int xptlink_main(int argc, char* argv[]);
|
|
int xptdump_main(int argc, char* argv[]);
|
|
|
|
FILE * FSp_fopen(ConstFSSpecPtr spec, const char * open_mode);
|
|
size_t mac_get_file_length(const char* filename);
|
|
}
|
|
|
|
/* external variables */
|
|
extern jmp_buf exit_jump;
|
|
extern int exit_status;
|
|
|
|
/* global variables */
|
|
CWPluginContext gPluginContext;
|
|
|
|
/* local variables */
|
|
static CWFileSpec gOutputDirectory;
|
|
static CWFileSpec gObjectCodeDirectory;
|
|
|
|
/*
|
|
* xpt_linker - main entry-point for linker plugin
|
|
*
|
|
*/
|
|
pascal short xpt_linker(CWPluginContext context)
|
|
{
|
|
long request;
|
|
if (CWGetPluginRequest(context, &request) != cwNoErr)
|
|
return cwErrRequestFailed;
|
|
|
|
gPluginContext = context;
|
|
short result = cwNoErr;
|
|
|
|
/* dispatch on linker request */
|
|
switch (request) {
|
|
case reqInitLinker:
|
|
/* linker has just been loaded into memory */
|
|
break;
|
|
|
|
case reqTermLinker:
|
|
/* linker is about to be unloaded from memory */
|
|
break;
|
|
|
|
case reqLink:
|
|
/* build the final executable */
|
|
result = Link(context);
|
|
break;
|
|
|
|
case reqDisassemble:
|
|
/* disassemble object code for a given project file */
|
|
result = Disassemble(context);
|
|
break;
|
|
|
|
case reqTargetInfo:
|
|
/* return info describing target characteristics */
|
|
result = GetTargetInfo(context);
|
|
break;
|
|
|
|
default:
|
|
result = cwErrRequestFailed;
|
|
break;
|
|
}
|
|
|
|
result = CWDonePluginRequest(context, result);
|
|
|
|
/* return result code */
|
|
return result;
|
|
}
|
|
|
|
static char* full_path_to(const FSSpec& file)
|
|
{
|
|
short len = 0;
|
|
Handle fullPath = NULL;
|
|
if (FSpGetFullPath(&file, &len, &fullPath) == noErr && fullPath != NULL) {
|
|
char* path = new char[1 + len];
|
|
if (path != NULL) {
|
|
BlockMoveData(*fullPath, path, len);
|
|
path[len] = '\0';
|
|
}
|
|
DisposeHandle(fullPath);
|
|
return path;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Provides the full path name to a given directory.
|
|
*/
|
|
static char* full_path_to(short vRefNum, long dirID)
|
|
{
|
|
long parID;
|
|
if (GetParentID(vRefNum, dirID, NULL, &parID) == noErr) {
|
|
FSSpec dirSpec = { vRefNum, parID };
|
|
if (GetDirName(vRefNum, dirID, dirSpec.name) == noErr) {
|
|
return full_path_to(dirSpec);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Returns the length of a file, assuming it is always located in the
|
|
* project's object code directory.
|
|
*/
|
|
size_t mac_get_file_length(const char* filename)
|
|
{
|
|
FSSpec fileSpec = { gObjectCodeDirectory.vRefNum, gObjectCodeDirectory.parID };
|
|
c2p_strcpy(fileSpec.name, filename);
|
|
long dataSize, rsrcSize;
|
|
if (FSpGetFileSize(&fileSpec, &dataSize, &rsrcSize) != noErr)
|
|
dataSize = 0;
|
|
return dataSize;
|
|
}
|
|
|
|
/**
|
|
* replaces standard fopen -- opens files for writing in the project's output directory,
|
|
* and files for reading in the object code directory.
|
|
*/
|
|
FILE* std::fopen(const char* filename, const char *mode)
|
|
{
|
|
CWFileSpec& fileDir = (mode[0] == 'r' ? gObjectCodeDirectory : gOutputDirectory);
|
|
FSSpec fileSpec = { fileDir.vRefNum, fileDir.parID };
|
|
c2p_strcpy(fileSpec.name, filename);
|
|
return FSp_fopen(&fileSpec, mode);
|
|
}
|
|
|
|
static CWResult GetSettings(CWPluginContext context, XPIDLSettings& settings)
|
|
{
|
|
CWMemHandle settingsHand;
|
|
CWResult err = CWGetNamedPreferences(context, kXPIDLPanelName, &settingsHand);
|
|
if (!CWSUCCESS(err))
|
|
return (err);
|
|
|
|
XPIDLSettings* settingsPtr = NULL;
|
|
err = CWLockMemHandle(context, settingsHand, false, (void**)&settingsPtr);
|
|
if (!CWSUCCESS(err))
|
|
return (err);
|
|
|
|
settings = *settingsPtr;
|
|
|
|
err = CWUnlockMemHandle(context, settingsHand);
|
|
if (!CWSUCCESS(err))
|
|
return (err);
|
|
|
|
return noErr;
|
|
}
|
|
|
|
static CWResult Link(CWPluginContext context)
|
|
{
|
|
long index;
|
|
CWResult err;
|
|
long filecount;
|
|
|
|
// load the relevant prefs.
|
|
XPIDLSettings settings = { kXPIDLSettingsVersion, kXPIDLModeTypelib, false, false };
|
|
err = GetSettings(context, settings);
|
|
if (err != cwNoErr)
|
|
return (err);
|
|
|
|
// find out how many files there are to link.
|
|
err = CWGetProjectFileCount(context, &filecount);
|
|
if (err != cwNoErr)
|
|
return (err);
|
|
|
|
// assemble the argument list.
|
|
// { "xpt_link", outputFile, inputFile1, ..., inputFileN, NULL }
|
|
char** argv = new char*[2 + filecount + 1];
|
|
int argc = 0;
|
|
argv[argc++] = "xpt_link";
|
|
|
|
// get the output directory.
|
|
err = CWGetOutputFileDirectory(context, &gOutputDirectory);
|
|
if (!CWSUCCESS(err))
|
|
return (err);
|
|
|
|
// get the object code directory.
|
|
err = CWGetStoredObjectFileSpec(context, 0, &gObjectCodeDirectory);
|
|
if (!CWSUCCESS(err))
|
|
return (err);
|
|
|
|
// push the output file name.
|
|
if ((argv[argc++] = p2c_strdup(settings.output)) == NULL)
|
|
return cwErrOutOfMemory;
|
|
|
|
for (index = 0; (err == cwNoErr) && (index < filecount); index++) {
|
|
// get the name of each output file.
|
|
CWFileSpec outputFile;
|
|
err = CWGetStoredObjectFileSpec(context, index, &outputFile);
|
|
if (err == cwNoErr) {
|
|
if ((argv[argc++] = p2c_strdup(outputFile.name)) == NULL) {
|
|
err = cwErrOutOfMemory;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (err != cwNoErr)
|
|
return err;
|
|
|
|
// trap calls to exit, which longjmp back to here.
|
|
if (setjmp(exit_jump) == 0) {
|
|
if (xptlink_main(argc, argv) != 0)
|
|
err = cwErrRequestFailed;
|
|
} else {
|
|
// evidently the good old exit function got called.
|
|
if (exit_status != 0)
|
|
err = cwErrRequestFailed;
|
|
}
|
|
|
|
return (err);
|
|
}
|
|
|
|
static CWResult Disassemble(CWPluginContext context)
|
|
{
|
|
CWResult err = noErr;
|
|
|
|
// cache the project's output directory.
|
|
err = CWGetOutputFileDirectory(gPluginContext, &gOutputDirectory);
|
|
if (!CWSUCCESS(err))
|
|
return (err);
|
|
|
|
long fileNum;
|
|
err = CWGetMainFileNumber(context, &fileNum);
|
|
if (!CWSUCCESS(err))
|
|
return (err);
|
|
|
|
// get the output file's location from the stored object data.
|
|
err = CWGetStoredObjectFileSpec(context, fileNum, &gObjectCodeDirectory);
|
|
if (!CWSUCCESS(err))
|
|
return (err);
|
|
|
|
char* outputName = p2c_strdup(gObjectCodeDirectory.name);
|
|
if (outputName == NULL)
|
|
return cwErrOutOfMemory;
|
|
|
|
XPIDLSettings settings = { kXPIDLSettingsVersion, kXPIDLModeTypelib, false, false };
|
|
GetSettings(context, settings);
|
|
|
|
// build an argument list and call xpt_dump.
|
|
int argc = 1;
|
|
char* argv[] = { "xpt_dump", NULL, NULL, NULL };
|
|
if (settings.verbose) argv[argc++] = "-v";
|
|
argv[argc++] = outputName;
|
|
|
|
// trap calls to exit, which longjmp back to here.
|
|
if (setjmp(exit_jump) == 0) {
|
|
if (xptdump_main(argc, argv) != 0)
|
|
err = cwErrRequestFailed;
|
|
} else {
|
|
// evidently the good old exit function got called.
|
|
if (exit_status != 0)
|
|
err = cwErrRequestFailed;
|
|
}
|
|
|
|
delete[] outputName;
|
|
|
|
if (err == noErr) {
|
|
// display the disassembly in its own fresh text window.
|
|
CWNewTextDocumentInfo info = {
|
|
NULL,
|
|
mac_console_handle,
|
|
false
|
|
};
|
|
CWResizeMemHandle(context, mac_console_handle, mac_console_count);
|
|
err = CWCreateNewTextDocument(context, &info);
|
|
}
|
|
|
|
return (err);
|
|
}
|
|
|
|
static CWResult GetTargetInfo(CWPluginContext context)
|
|
{
|
|
CWTargetInfo targ;
|
|
memset(&targ, 0, sizeof(targ));
|
|
|
|
CWResult err = CWGetOutputFileDirectory(context, &targ.outfile);
|
|
targ.outputType = linkOutputFile;
|
|
targ.symfile = targ.outfile; /* location of SYM file */
|
|
targ.linkType = exelinkageFlat;
|
|
targ.targetCPU = '****';
|
|
targ.targetOS = '****';
|
|
|
|
// load the relevant settings.
|
|
XPIDLSettings settings = { kXPIDLSettingsVersion, kXPIDLModeTypelib, false, false };
|
|
err = GetSettings(context, settings);
|
|
if (err != cwNoErr)
|
|
return (err);
|
|
|
|
#if CWPLUGIN_HOST == CWPLUGIN_HOST_MACOS
|
|
// tell the IDE about the output file.
|
|
targ.outfileCreator = 'MMCH';
|
|
targ.outfileType = 'CWIE';
|
|
targ.debuggerCreator = kDebuggerCreator; /* so IDE can locate our debugger */
|
|
|
|
BlockMoveData(settings.output, targ.outfile.name, 1 + settings.output[0]);
|
|
targ.symfile.name[0] = 0;
|
|
#endif
|
|
|
|
#if CWPLUGIN_HOST == CWPLUGIN_HOST_WIN32
|
|
targ.debugHelperIsRegKey = true;
|
|
*(long*)targ.debugHelperName = kDebuggerCreator;
|
|
targ.debugHelperName[4] = 0;
|
|
strcat(targ.outfile.path, "\\");
|
|
strcat(targ.outfile.path, prefsData.outfile);
|
|
strcpy(targ.symfile.path, targ.outfile.path);
|
|
strcat(targ.symfile.path, ".SYM");
|
|
#endif
|
|
|
|
targ.runfile = targ.outfile;
|
|
targ.linkAgainstFile = targ.outfile;
|
|
|
|
/* we can only run applications */
|
|
// targ.canRun = (prefsData.projtype == kProjTypeApplication);
|
|
|
|
/* we can only debug if we have a SYM file */
|
|
// targ.canDebug = prefsData.linksym;
|
|
|
|
err = CWSetTargetInfo(context, &targ);
|
|
|
|
return err;
|
|
}
|
|
|
|
#if 0
|
|
|
|
#if CW_USE_PRAGMA_EXPORT
|
|
#pragma export on
|
|
#endif
|
|
|
|
CWPLUGIN_ENTRY(CWPlugin_GetDropInFlags)(const DropInFlags** flags, long* flagsSize)
|
|
{
|
|
static const DropInFlags sFlags = {
|
|
kCurrentDropInFlagsVersion,
|
|
CWDROPINLINKERTYPE,
|
|
DROPINCOMPILERLINKERAPIVERSION_7,
|
|
(linkMultiTargAware | linkAlwaysReload),
|
|
0,
|
|
DROPINCOMPILERLINKERAPIVERSION
|
|
};
|
|
|
|
*flags = &sFlags;
|
|
*flagsSize = sizeof(sFlags);
|
|
|
|
return cwNoErr;
|
|
}
|
|
|
|
CWPLUGIN_ENTRY(CWPlugin_GetDropInName)(const char** dropinName)
|
|
{
|
|
static const char* sDropInName = "xpt Linker";
|
|
*dropinName = sDropInName;
|
|
return cwNoErr;
|
|
}
|
|
|
|
CWPLUGIN_ENTRY(CWPlugin_GetDisplayName)(const char** displayName)
|
|
{
|
|
static const char* sDisplayName = "xpt Linker";
|
|
*displayName = sDisplayName;
|
|
return cwNoErr;
|
|
}
|
|
|
|
CWPLUGIN_ENTRY(CWPlugin_GetPanelList)(const CWPanelList** panelList)
|
|
{
|
|
// +++Turn this on when the sample panel has been converted!
|
|
static const char* sPanelName = kXPIDLPanelName;
|
|
static CWPanelList sPanelList = { kCurrentCWPanelListVersion, 1, &sPanelName };
|
|
|
|
*panelList = &sPanelList;
|
|
|
|
return cwNoErr;
|
|
}
|
|
|
|
CWPLUGIN_ENTRY(CWPlugin_GetTargetList)(const CWTargetList** targetList)
|
|
{
|
|
static CWDataType sCPU = '****';
|
|
static CWDataType sOS = '****';
|
|
static CWTargetList sTargetList = { kCurrentCWTargetListVersion, 1, &sCPU, 1, &sOS };
|
|
|
|
*targetList = &sTargetList;
|
|
|
|
return cwNoErr;
|
|
}
|
|
|
|
CWPLUGIN_ENTRY(CWPlugin_GetDefaultMappingList)(const CWExtMapList** defaultMappingList)
|
|
{
|
|
static CWExtensionMapping sExtension = { 'MMCH', ".xpt", 0 };
|
|
static CWExtMapList sExtensionMapList = { kCurrentCWExtMapListVersion, 1, &sExtension };
|
|
|
|
*defaultMappingList = &sExtensionMapList;
|
|
|
|
return cwNoErr;
|
|
}
|
|
|
|
CWPLUGIN_ENTRY (CWPlugin_GetFamilyList)(const CWFamilyList** familyList)
|
|
{
|
|
static CWFamily sFamily = { 'XIDL', "xpidl Settings" };
|
|
static CWFamilyList sFamilyList = { kCurrentCWFamilyListVersion, 0, &sFamily };
|
|
|
|
*familyList = &sFamilyList;
|
|
|
|
return cwNoErr;
|
|
}
|
|
|
|
#if CW_USE_PRAGMA_EXPORT
|
|
#pragma export off
|
|
#endif
|
|
|
|
#endif
|