// Created by Jaime O. Rios
#import "P4Functionality.h"
#import "P4XcodeHelper.h"
#import <string>
#import <algorithm>
// http://stackoverflow.com/questions/478898/how-to-execute-a-command-and-get-output-of-command-within-c
static std::string exec(const char* cmd)
{
auto result = std::string("");
auto pipe = popen(cmd, "r");
if (pipe)
{
const auto buffLength = 256;
char buffer[buffLength];
#if defined(_DEBUG)
auto error = 0;
auto errNo = 0;
#endif
while(!feof(pipe))
{
auto stringBuf = fgets(buffer, buffLength, pipe);
if( stringBuf != nullptr)
{
result += buffer;
}
#if defined(_DEBUG)
else if (result.length() == 0)
{
error = ferror(pipe);
if(error != 0)
{
errNo = errno;
(void)error;
(void)errNo;
perror("Error ");
}
}
#endif
}
pclose(pipe);
}
return result;
}
static void remove_newline(std::string& str)
{
auto newline = std::string("\n");
auto found = str.find_last_not_of(newline);
if (found!=std::string::npos)
{
str.erase(found+1);
}
else
{
str.clear();
}
}
@implementation P4Functionality
- (instancetype)init
{
self = [super init];
if (self)
{
_userHomePathSettings = @".bash_login";
[self updateP4Status];
[self updateP4VCStatus];
_P4CONFIG_Set = NO;
_p4ClientName = nil;
}
return self;
}
- (void)updateP4Status
{
_p4Path = [self getPathToCommandLineApp:(const char*)"which p4"];
_p4Installed = _p4Path != nil ? YES : NO;
}
- (void)updateP4VCStatus
{
_p4vcPath = [self getPathToCommandLineApp:(const char*)"which p4vc"];
_p4vcInstalled = _p4vcPath != nil ? YES : NO;
}
- (NSString*)getPathToCommandLineApp:(const char*)string
{
NSString* commandPath = nil;
auto command = std::string(string);
auto returnVal = exec(command.c_str());
if (returnVal.length())
{
remove_newline(returnVal);
commandPath = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding];
}
return commandPath;
}
- (void)checkSettings
{
[self p4ClientSet];
}
/**
Precondition(s):
- _p4Path and _userHomePathSettings must be non-nil and valid
Postcondition(s):
- Returns p4 command return string
- Returns empty std::string on error
*/
- (std::string)execP4CommandForProjectAtPath:(std::string)p4Command workingPath:(const char*)workingPath
{
if (_p4Path == nil || _userHomePathSettings == nil)
{
return std::string("");
}
auto userSettingsFile = [_userHomePathSettings cStringUsingEncoding:NSASCIIStringEncoding];
auto p4Path = [_p4Path cStringUsingEncoding:NSASCIIStringEncoding];
auto command = std::string("");
command = "source ~/";
command += userSettingsFile;
command += "; ";
command += "cd ";
command += workingPath;
command += "; ";
command += p4Path;
command += p4Command;
return exec(command.c_str());
}
- (BOOL)p4ClientSet
{
if (_p4Path == nil)
{
[self updateP4Status];
}
if (_p4vcPath == nil)
{
[self updateP4VCStatus];
}
BOOL commandSuccess = NO;
if(_p4Path != nil)
{
auto projectURL = [P4XcodeHelper currentProjectURL];
if(projectURL != nil)
{
auto projectPath = [[projectURL path] cStringUsingEncoding:NSASCIIStringEncoding];
auto p4Command = std::string(" set | grep P4CLIENT");
auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:projectPath];
if (returnVal.length())
{
_P4CONFIG_Set = YES;
_p4ClientName = [NSString stringWithCString:returnVal.c_str()
encoding:NSASCIIStringEncoding];
commandSuccess = YES;
}
}
}
if(commandSuccess == NO)
{
_P4CONFIG_Set = NO;
auto noClientName = std::string("No client name defined");
_p4ClientName = [NSString stringWithCString:noClientName.c_str()
encoding:NSASCIIStringEncoding];
}
return commandSuccess;
}
- (NSString*)p4EditCurrentFile
{
NSString* checkoutSuccess = nil;
if (_p4Path == nil || _p4ClientName == nil)
{
[self checkSettings];
}
if (_p4Path != nil && _p4ClientName != nil)
{
IDESourceCodeDocument *currentSourceCodeDocument = [P4XcodeHelper currentSourceCodeDocument];
NSString *filePath = [[currentSourceCodeDocument fileURL] path];
if (filePath)
{
NSString* fileName = [filePath lastPathComponent];
NSString* parentFolder = [filePath stringByDeletingLastPathComponent];
auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding];
auto p4Command = std::string(" edit ");
p4Command += [fileName cStringUsingEncoding:NSASCIIStringEncoding];
auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder];
checkoutSuccess = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding];
}
}
return checkoutSuccess;
}
- (NSString*)p4Add
{
NSString* addFileSuccess = nil;
if (_p4Path == nil || _p4ClientName == nil)
{
[self checkSettings];
}
if (_p4Path != nil && _p4ClientName != nil)
{
IDESourceCodeDocument *currentSourceCodeDocument = [P4XcodeHelper currentSourceCodeDocument];
NSString *filePath = [[currentSourceCodeDocument fileURL] path];
if (filePath)
{
NSString* parentFolder = [filePath stringByDeletingLastPathComponent];
NSString* fileName = [filePath lastPathComponent];
auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding];
auto p4Command = std::string(" add ");
p4Command += [fileName cStringUsingEncoding:NSASCIIStringEncoding];
auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder];
addFileSuccess = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding];
}
}
return addFileSuccess;
}
- (NSString*)p4Info
{
if (_p4Path == nil)
{
[self checkSettings];
}
if (_p4Path == nil)
{
return nil;
}
IDESourceCodeDocument *currentSourceCodeDocument = [P4XcodeHelper currentSourceCodeDocument];
if(currentSourceCodeDocument == nil)
{
return nil;
}
NSString* p4InfoResult = nil;
NSString *filePath = [[currentSourceCodeDocument fileURL] path];
if (filePath)
{
NSString* parentFolder = [filePath stringByDeletingLastPathComponent];
auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding];
auto p4Command = std::string(" info");
auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder];
p4InfoResult = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding];
}
return p4InfoResult;
}
- (void)p4vcRevGraph
{
if (_p4vcPath == nil || _p4ClientName == nil)
{
[self checkSettings];
}
if (_p4vcPath != nil && _p4ClientName != nil)
{
IDESourceCodeDocument *currentSourceCodeDocument = [P4XcodeHelper currentSourceCodeDocument];
NSString *filePath = [[currentSourceCodeDocument fileURL] path];
if (filePath)
{
std::string command("");
std::string returnVal("");
NSString* parentFolder = [filePath stringByDeletingLastPathComponent];
NSString* fileName = [filePath lastPathComponent];
command = "source ~/";
command += [_userHomePathSettings cStringUsingEncoding:NSASCIIStringEncoding];
command += "; cd ";
command += [parentFolder cStringUsingEncoding:NSASCIIStringEncoding];
command += "; ";
command += [_p4vcPath cStringUsingEncoding:NSASCIIStringEncoding];
command += " revgraph ";
command += [fileName cStringUsingEncoding:NSASCIIStringEncoding];
returnVal = exec(command.c_str());
NSLog(@"Return value: %s\n", returnVal.c_str());
}
}
}
- (void)p4vcTimeLapse
{
if (_p4vcPath == nil || _p4ClientName == nil)
{
[self checkSettings];
}
if (_p4vcPath != nil && _p4ClientName != nil)
{
IDESourceCodeDocument *currentSourceCodeDocument = [P4XcodeHelper currentSourceCodeDocument];
NSString *filePath = [[currentSourceCodeDocument fileURL] path];
if (filePath)
{
std::string command("");
std::string returnVal("");
NSString* parentFolder = [filePath stringByDeletingLastPathComponent];
NSString* fileName = [filePath lastPathComponent];
command = "source ~/";
command += [_userHomePathSettings cStringUsingEncoding:NSASCIIStringEncoding];
command += "; cd ";
command += [parentFolder cStringUsingEncoding:NSASCIIStringEncoding];
command += "; ";
command += [_p4vcPath cStringUsingEncoding:NSASCIIStringEncoding];
command += " timelapse ";
command += [fileName cStringUsingEncoding:NSASCIIStringEncoding];
returnVal = exec(command.c_str());
NSLog(@"Return value: %s\n", returnVal.c_str());
}
}
}
#pragma mark -
- (NSString*)issueP4EditForFile:(NSString*)fileToBeCheckedOut
{
if(fileToBeCheckedOut == nil)
{
return @"ERROR: p4 edit operation requires a valid file path to work";
}
NSString* fileName = [fileToBeCheckedOut lastPathComponent];
NSString* parentFolder = [fileToBeCheckedOut stringByDeletingLastPathComponent];
auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding];
auto p4Command = std::string(" edit ");
p4Command += [fileName cStringUsingEncoding:NSASCIIStringEncoding];
auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder];
NSString* editRes = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding];
return editRes;
}
- (NSString*)issueP4AddForFile:(NSString*)fileToBeAdded
{
if(fileToBeAdded == nil)
{
return @"ERROR: p4 add operation requires a valid file path to work";
}
NSString* parentFolder = [fileToBeAdded stringByDeletingLastPathComponent];
NSString* fileName = [fileToBeAdded lastPathComponent];
auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding];
auto p4Command = std::string(" add ");
p4Command += [fileName cStringUsingEncoding:NSASCIIStringEncoding];
auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder];
NSString* addFileRes = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding];
return addFileRes;
}
- (NSString*)issueP4InfoForFolder:(NSString*)folderPath
{
if(folderPath == nil)
{
return @"ERROR: p4 info operation requires a valid folder path to work";
}
auto workingFolder = [folderPath cStringUsingEncoding:NSASCIIStringEncoding];
auto p4Command = std::string(" info");
auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder];
NSString* p4InfoResult = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding];
return p4InfoResult;
}
- (NSString*)displayP4VCRevGraphForFileAtPath:(NSString*)filePath
{
if(filePath == nil)
{
return nil;
}
NSString* parentFolder = [filePath stringByDeletingLastPathComponent];
NSString* fileName = [filePath lastPathComponent];
auto command = std::string("source ~/");
command += [_userHomePathSettings cStringUsingEncoding:NSASCIIStringEncoding];
command += "; cd ";
command += [parentFolder cStringUsingEncoding:NSASCIIStringEncoding];
command += "; ";
command += [_p4vcPath cStringUsingEncoding:NSASCIIStringEncoding];
command += " revgraph ";
command += [fileName cStringUsingEncoding:NSASCIIStringEncoding];
auto returnVal = exec(command.c_str());
NSString* messageString = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding];
return messageString;
}
- (NSString*)displayP4VCTimeLapseForFileAtPath:(NSString*)filePath
{
if(filePath == nil)
{
return nil;
}
NSString* parentFolder = [filePath stringByDeletingLastPathComponent];
NSString* fileName = [filePath lastPathComponent];
auto command = std::string("source ~/");
command += [_userHomePathSettings cStringUsingEncoding:NSASCIIStringEncoding];
command += "; cd ";
command += [parentFolder cStringUsingEncoding:NSASCIIStringEncoding];
command += "; ";
command += [_p4vcPath cStringUsingEncoding:NSASCIIStringEncoding];
command += " timelapse ";
command += [fileName cStringUsingEncoding:NSASCIIStringEncoding];
auto returnVal = exec(command.c_str());
NSString* messageString = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding];
return messageString;
}
@end
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #10 | 20141 | Jaime Rios | Merging using M_robc_apple-to_jaime_rios_XcodePerforcePlugin | ||
| #9 | 16809 | Jaime Rios | Minor editing changes | ||
| #8 | 16549 | Jaime Rios | Added debugging code to see paths exported by Xcode; modified MD readme file to include preferred location of p4 and p4vc binaries | ||
| #7 | 15948 | Jaime Rios | Added p4vc submit functionality; violated rule 0; updated plist for xcode 7 uuid; fixed errors encountered by xcode related to optionals and interoperability with Obj-C++ code. | ||
| #6 | 11740 | Jaime Rios | Updated readme text with additional known issues; refactored p4 revert function; added perforce icon to alert message. | ||
| #5 | 11739 | Jaime Rios |
Merging //guest/matt_attaway/XcodePerforcePlugin/... to //guest/jaime_rios/XcodePerforcePlugin/... |
||
| #4 | 11737 | Jaime Rios | Minor change to error checking in debug mode; updated readme document with information pertaining to how to set up settings for workspace and edited MD markup formating. | ||
| #3 | 11733 | Jaime Rios | Refactored code to have more functionality within Swift code; fixed perforce connection bugs. | ||
| #2 | 11714 | Jaime Rios | Added unit tests; fixed bugs found during testing; added Jon Reid's XcodeCoverage files http://qualitycoding.org/xcode-code-coverage/ modified project to produce code coverage reports. | ||
| #1 | 11694 | Jaime Rios | Initial add of XcodePerforcePlugin project to guest depot. |