mirror of
https://github.com/QuasarApp/macdependency.git
synced 2025-04-27 21:04:31 +00:00
281 lines
8.5 KiB
Plaintext
281 lines
8.5 KiB
Plaintext
//
|
|
// MachOModel.m
|
|
// MacDependency
|
|
//
|
|
// Created by Konrad Windszus on 13.07.09.
|
|
// Copyright 2009 Konrad Windszus. All rights reserved.
|
|
//
|
|
|
|
#import "MachOModel.h"
|
|
#import "MyDocument.h"
|
|
|
|
#import "ConversionStdString.h"
|
|
#import "VersionFormatter.h"
|
|
#import "ArchitectureModel.h"
|
|
|
|
#include "MachO/machoexception.h"
|
|
#include "MachO/machoheader.h"
|
|
#include "MachO/symboltablecommand.h"
|
|
|
|
|
|
// declare private methods
|
|
@interface MachOModel ()
|
|
- (void) initChildren;
|
|
- (void) checkConstraints:(DylibCommand*) dylibId;
|
|
- (void) setStateWithWarning:(BOOL)isWarning;
|
|
- (id) initWithFile:(MachO*)file command:(DylibCommand*)command document:(MyDocument*)document parent:(MachOModel*)parent architecture:(MachOArchitecture*)architecture;
|
|
@end
|
|
|
|
@implementation MachOModel
|
|
|
|
- (id) initWithFile:(MachO*)aFile document:(MyDocument*)aDocument architecture:(MachOArchitecture*)anArchitecture loadChildren:(BOOL)loadChildren {
|
|
state = StateNormal;
|
|
if (!(self = [self initWithFile:aFile command:nil document:aDocument parent:nil architecture:anArchitecture])) return nil;
|
|
if (loadChildren) {
|
|
[self initChildren];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
// called by initChildren (calls initWithFile internally)
|
|
- (id) initWithFilename:(std::string&)filename command:(DylibCommand*)aCommand document:(MyDocument*)aDocument parent:(MachOModel*)aParent {
|
|
BOOL isWeakReference = (command && !command->isNecessary());
|
|
MachO* aFile = nullptr;
|
|
MachOArchitecture* anArchitecture = nullptr;
|
|
state = StateNormal;
|
|
try {
|
|
aFile = [aDocument cache]->getFile(filename, aParent->file); // throws exception in case file is not found
|
|
anArchitecture = aFile->getCompatibleArchitecture(aParent->architecture);
|
|
if (!anArchitecture) {
|
|
[self setStateWithWarning:isWeakReference];
|
|
NSString* log = [NSString stringWithFormat:NSLocalizedString(@"ERR_ARCHITECTURE_MISMATCH", nil), filename.c_str(), [parent name]];
|
|
[aDocument appendLogLine:log withModel:self state:state];
|
|
}
|
|
|
|
} catch (MachOException& exc) {
|
|
[self setStateWithWarning:isWeakReference];
|
|
NSString* log = [NSString stringWithStdString:exc.getCause()];
|
|
[aDocument appendLogLine:log withModel:self state:state];
|
|
// distinguish between weak and strong. In both cases append to tree with a status color
|
|
}
|
|
[aDocument incrementNumDependencies];
|
|
if (!(self = [self initWithFile:aFile command:aCommand document:aDocument parent:aParent architecture:anArchitecture])) return nil;
|
|
return self;
|
|
}
|
|
|
|
// private
|
|
- (id) initWithFile:(MachO*)aFile command:(DylibCommand*)aCommand document:(MyDocument*)aDocument parent:(MachOModel*)aParent architecture:(MachOArchitecture*)anArchitecture {
|
|
if (self = [super init]) {
|
|
document = aDocument;
|
|
parent = aParent;
|
|
command = aCommand;
|
|
file = aFile;
|
|
children = nil;
|
|
architecture = anArchitecture;
|
|
if (architecture) {
|
|
DylibCommand* dylibId = architecture->getDynamicLibIdCommand();
|
|
if (dylibId && command) {
|
|
[self checkConstraints:dylibId];
|
|
}
|
|
}
|
|
|
|
} return self;
|
|
}
|
|
|
|
|
|
- (void) setStateWithWarning:(BOOL)isWarning {
|
|
State newState = StateError;
|
|
if (isWarning) {
|
|
newState = StateWarning;
|
|
}
|
|
|
|
if (self->state < newState) {
|
|
self->state = newState;
|
|
}
|
|
}
|
|
|
|
- (id) parent {
|
|
return parent;
|
|
}
|
|
|
|
- (void) checkConstraints:(DylibCommand*) dylibId {
|
|
// check version information (only compatible version information is relevant)
|
|
unsigned int minVersion = dylibId->getCompatibleVersion();
|
|
unsigned int requestedMinVersion = command->getCompatibleVersion();
|
|
unsigned int requestedMaxVersion = command->getCurrentVersion();
|
|
|
|
VersionFormatter* versionFormatter = [[VersionFormatter alloc] init];
|
|
|
|
BOOL isWeakReference = (command && !command->isNecessary());
|
|
|
|
NSString* log;
|
|
// check minimum version
|
|
if (minVersion != 0 && requestedMinVersion != 0 && minVersion < requestedMinVersion) {
|
|
[self setStateWithWarning:isWeakReference];
|
|
log = [NSString stringWithFormat:NSLocalizedString(@"ERR_MINIMUM_VERSION", nil), parent->command->getName().c_str(), [parent name], [versionFormatter stringForObjectValue:[NSNumber numberWithUnsignedInt:requestedMinVersion]], [versionFormatter stringForObjectValue:[NSNumber numberWithUnsignedInt:minVersion]]];
|
|
[document appendLogLine:log withModel:self state:state];
|
|
|
|
}
|
|
|
|
// extended checks which are currently not done by dyld
|
|
|
|
// check maximum version
|
|
if (minVersion != 0 && requestedMaxVersion != 0 && minVersion > requestedMaxVersion) {
|
|
[self setStateWithWarning:YES];
|
|
log = [NSString stringWithFormat:NSLocalizedString(@"ERR_MAXIMUM_VERSION", nil), command->getName().c_str(), [parent name], [versionFormatter stringForObjectValue:[NSNumber numberWithUnsignedInt:requestedMaxVersion]], [versionFormatter stringForObjectValue:[NSNumber numberWithUnsignedInt:minVersion]]];
|
|
[document appendLogLine:log withModel:self state:state];
|
|
|
|
}
|
|
|
|
// check names
|
|
if (dylibId->getName() != command->getName()) {
|
|
[self setStateWithWarning:YES];
|
|
log = [NSString stringWithFormat:NSLocalizedString(@"ERR_NAME_MISMATCH", nil), command->getName().c_str(), [parent name], dylibId->getName().c_str()];
|
|
[document appendLogLine:log withModel:self state:state];
|
|
}
|
|
|
|
// TODO: check for relative paths, since they can lead to problems
|
|
}
|
|
|
|
- (NSArray*)children {
|
|
if (children == nil) {
|
|
[self initChildren];
|
|
}
|
|
return children;
|
|
}
|
|
|
|
- (void) initChildren {
|
|
// TODO: tweak capacity
|
|
children = [NSMutableArray arrayWithCapacity:20];
|
|
if (architecture) {
|
|
std::string workingDirectory = [[document workingDirectory] stdString];
|
|
for (MachOArchitecture::LoadCommandsConstIterator it = architecture->getLoadCommandsBegin();
|
|
it != architecture->getLoadCommandsEnd();
|
|
++it) {
|
|
|
|
LoadCommand* childLoadCommand = (*it);
|
|
// check if it is dylibcommand
|
|
DylibCommand* dylibCommand = dynamic_cast<DylibCommand*> (childLoadCommand);
|
|
if (dylibCommand != NULL && !dylibCommand->isId()) {
|
|
std::string filename = architecture->getResolvedName(dylibCommand->getName(), workingDirectory);
|
|
|
|
[children addObject: [[MachOModel alloc] initWithFilename: filename command: dylibCommand document: document parent: self]];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (BOOL)isLeaf {
|
|
[self children];
|
|
if ([children count] == 0)
|
|
return YES;
|
|
return NO;
|
|
}
|
|
|
|
- (NSColor*) textColor {
|
|
NSColor* color;
|
|
switch(state) {
|
|
case StateWarning:
|
|
color = [NSColor blueColor];
|
|
break;
|
|
case StateError:
|
|
color = [NSColor redColor];
|
|
break;
|
|
default:
|
|
color= [NSColor blackColor];
|
|
}
|
|
return color;
|
|
}
|
|
|
|
- (NSString*) filename {
|
|
std::string filename;
|
|
if (file)
|
|
filename = file->getFileName();
|
|
return [NSString stringWithStdString:filename];
|
|
}
|
|
|
|
- (NSString*) version {
|
|
if (file) {
|
|
return [NSString stringWithStdString:file->getVersion()];
|
|
}
|
|
return @"";
|
|
}
|
|
|
|
- (NSNumber*) size {
|
|
if (file != nullptr)
|
|
return @(file->getSize());
|
|
return @(0);
|
|
}
|
|
|
|
- (NSString*) name {
|
|
std::string name;
|
|
if (command) {
|
|
name = command->getName();
|
|
} else if (file) {
|
|
name = file->getName();
|
|
}
|
|
return [NSString stringWithStdString:name];
|
|
}
|
|
|
|
- (NSNumber*) currentVersion {
|
|
if (command) {
|
|
return [NSNumber numberWithUnsignedInt:command->getCurrentVersion()];
|
|
}
|
|
return [NSNumber numberWithUnsignedInt:0];
|
|
}
|
|
|
|
- (NSNumber*) compatibleVersion {
|
|
if (command) {
|
|
return [NSNumber numberWithUnsignedInt:command->getCompatibleVersion()];
|
|
}
|
|
return [NSNumber numberWithUnsignedInt:0];
|
|
}
|
|
|
|
- (NSDate*) lastModificationTime {
|
|
if (file)
|
|
return [NSDate dateWithTimeIntervalSince1970:file->getLastModificationTime()];
|
|
return NULL;
|
|
}
|
|
|
|
- (NSString*) dependencyType {
|
|
NSString* type;
|
|
if (!command) {
|
|
return @"";
|
|
}
|
|
switch(command->getType()) {
|
|
|
|
case DylibCommand::DependencyWeak:
|
|
type = NSLocalizedString(@"DEPENDENCY_TYPE_WEAK", nil);
|
|
break;
|
|
case DylibCommand::DependencyDelayed:
|
|
type = NSLocalizedString(@"DEPENDENCY_TYPE_DELAYED", nil);
|
|
break;
|
|
case DylibCommand::DependencyNormal:
|
|
type = NSLocalizedString(@"DEPENDENCY_TYPE_NORMAL", nil);
|
|
break;
|
|
default:
|
|
type = NSLocalizedString(@"UNDEFINED", @"Unknown");
|
|
}
|
|
return type;
|
|
}
|
|
|
|
- (NSArray*) architectures {
|
|
NSMutableArray* architectures = [NSMutableArray arrayWithCapacity:4];
|
|
|
|
if (file) {
|
|
for (MachO::MachOArchitecturesIterator iter = file->getArchitecturesBegin(); iter != file->getArchitecturesEnd(); iter++) {
|
|
// create model for architecture
|
|
ArchitectureModel* currentArchitecture = [[ArchitectureModel alloc]initWithArchitecture:(*iter) file:file document:document isRoot:NO];
|
|
// correct order (current architecture should have first index)
|
|
if ((*iter) == architecture) {
|
|
[architectures insertObject:currentArchitecture atIndex:0]; // insert at beginning
|
|
} else {
|
|
[architectures addObject:currentArchitecture]; // insert at end
|
|
}
|
|
|
|
}
|
|
}
|
|
return architectures;
|
|
}
|
|
@end
|