P4Functionality.mm #7

  • //
  • guest/
  • jaime_rios/
  • XcodePerforcePlugin/
  • XcodePerforcePlugin/
  • P4Functionality.mm
  • View
  • Commits
  • Open Download .zip Download (15 KB)
// 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*)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*)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
#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.