// 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 = std::shared_ptr<FILE>(popen(cmd, "r"), pclose); if (pipe.get()) { const auto buffLength = 256; char buffer[buffLength]; #if defined(_DEBUG) auto error = 0; auto errNo = 0; #endif while(!feof(pipe.get())) { auto stringBuf = fgets(buffer, buffLength, pipe.get()); if( stringBuf != nullptr) { result += buffer; } #if defined(_DEBUG) else if (result.length() == 0) { error = ferror(pipe.get()); if(error != 0) { errNo = errno; (void)error; (void)errNo; perror("Error "); } } #endif } } 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"]; #if _DEBUG && (0) // added to diagnose path issue if(_p4Path==nil) { // I would like to see what the environment has to say NSString* environmentVars = [self getPathToCommandLineApp:(const char*)"env"]; (void)environmentVars; // this line suppresses the unused variable warning int x = 0; ++x; // for breakpoints } #endif _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; } /* As you see, this function is just a proxy for the real function */ - (void)checkSettings { const BOOL routineResult = [self p4ClientSet]; (void)routineResult; // used for debug mode } /** 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*)p4Revert { NSString* revertFileSuccess = 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(" revert "); p4Command += [fileName cStringUsingEncoding:NSASCIIStringEncoding]; auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder]; revertFileSuccess = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; } } return revertFileSuccess; } - (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* result = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return result; } - (NSString* _Nonnull)issueP4SubmitForFile:(NSString* _Nonnull)fileToBeSubmitted withDescription:(NSString * _Nonnull)submitDescription { NSString* fileName = [fileToBeSubmitted lastPathComponent]; NSString* parentFolder = [fileToBeSubmitted stringByDeletingLastPathComponent]; auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = std::string(" submit -d \""); p4Command += [submitDescription cStringUsingEncoding:NSASCIIStringEncoding]; p4Command += std::string("\" "); p4Command += [fileName cStringUsingEncoding:NSASCIIStringEncoding]; auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder]; NSString* result = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return result; } - (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* result = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return result; } - (NSString*)issueP4RevertForFile:(NSString*)fileToBeReverted { if(fileToBeReverted == nil) { return @"ERROR: p4 add operation requires a valid file path to work"; } NSString* parentFolder = [fileToBeReverted stringByDeletingLastPathComponent]; NSString* fileName = [fileToBeReverted lastPathComponent]; auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = std::string(" revert "); p4Command += [fileName cStringUsingEncoding:NSASCIIStringEncoding]; auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder]; NSString* result = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return result; } - (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* result = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return result; } - (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 | |
---|---|---|---|---|---|
#2 | 20135 | robc_apple |
Fixed error when spaces in path Added the ability to checkout project files as well as xib and plist files. Added warning before revert happens Added Submit P4 with UI for description of submit |
||
#1 | 20134 | robc_apple |
Populate -o //guest/jaime_rios/XcodePerforcePlugin/... //guest/robc_apple/XcodePerforcePlugin/.... |
||
//guest/jaime_rios/XcodePerforcePlugin/XcodePerforcePlugin/P4Functionality.mm | |||||
#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. |