Changeset 39514


Ignore:
Timestamp:
Aug 22, 2008, 5:56:11 PM (12 years ago)
Author:
armahg@…
Message:

Added MPNotifications* files for asynchronous notifications between MacPorts.Framework and MPHelperTool. This code only covers the MPHelperTool side of things. It currently compiles and runs fine with the Test bundle even though there is no indication that DoShout() command is being called

Location:
branches/gsoc08-framework/MacPorts_Framework_Release
Files:
3 added
2 edited

Legend:

Unmodified
Added
Removed
  • branches/gsoc08-framework/MacPorts_Framework_Release/MPHelperTool.m

    r39309 r39514  
    4242#include "MPHelperCommon.h"
    4343
     44#include "MPHelperNotificationsProtocol.h"
     45#include "MPHelperNotificationsCommon.h"
     46
     47#include <stdlib.h>
     48#include <assert.h>
     49#include <unistd.h>
     50#include <sys/socket.h>
     51#include <sys/un.h>
     52#include <signal.h>
     53
    4454
    4555
     
    5161int notificationsFileDescriptor;
    5262BOOL hasSetFileDescriptor = NO;
     63
     64
     65
     66#pragma mark -
     67/////////////////////////////////////////////////////////////////
     68#pragma mark ***** Notifications Connection Abstraction
     69
     70// A ConnectionRef represents a connection from the client to the server.
     71// The internals of this are opaque to external callers.  All operations on
     72// a connection are done via the routines in this section.
     73
     74enum {
     75    kConnectionStateMagic = 'LCCM'           // Local Client Connection Magic
     76};
     77
     78typedef struct ConnectionState *  ConnectionRef;
     79// Pseudo-opaque reference to the connection.
     80
     81typedef Boolean (*ConnectionCallbackProcPtr)(
     82                                                                                         ConnectionRef           conn,
     83                                                                                         const PacketHeader *    packet,
     84                                                                                         void *                  refCon
     85                                                                                         );
     86// When the client enables listening on a connection, it supplies a
     87// function of this type as a callback.  We call this function in
     88// the context of the runloop specified by the client when they enable
     89// listening.
     90//
     91// conn is a reference to the connection.  It will not be NULL.
     92//
     93// packet is a pointer to the packet that arrived, or NULL if we've
     94// detected that the connection to the server is broken.
     95//
     96// refCon is a value that the client specified when it registered this
     97// callback.
     98//
     99// If the server sends you a bad packet, you can return false to
     100// tell the connection management system to shut down the connection.
     101
     102// ConnectionState is the structure used to track a single connection to
     103// the server.  All fields after fSockFD are only relevant if the client
     104// has enabled listening.
     105
     106struct ConnectionState {
     107    OSType                      fMagic;             // kConnectionStateMagic
     108    int                         fSockFD;            // UNIX domain socket to server
     109    CFSocketRef                 fSockCF;            // CFSocket wrapper for the above
     110    CFRunLoopSourceRef          fRunLoopSource;     // runloop source for the above
     111    CFMutableDataRef            fBufferedPackets;   // buffer for incomplete packet data
     112    ConnectionCallbackProcPtr   fCallback;          // client's packet callback
     113    void *                      fCallbackRefCon;    // refCon for the above.
     114};
     115
     116// Forward declarations.  See the comments associated with the function definition.
     117
     118static void ConnectionShutdown(ConnectionRef conn);
     119static void ConnectionCloseInternal(ConnectionRef conn, Boolean sayGoodbye);
     120
     121enum {
     122    kResultColumnWidth = 10
     123};
     124
     125static int ConnectionSend(ConnectionRef conn, const PacketHeader *packet)
     126// Send a packet to the server.  Use this when you're not expecting a
     127// reply.
     128//
     129// conn must be a valid connection
     130// packet must be a valid, ready-to-send, packet
     131// Returns an errno-style error code
     132{
     133    int     err;
     134   
     135    assert(conn != NULL);
     136    assert(conn->fSockFD != -1);            // connection must not be shut down
     137    // conn->fSockCF may or may not be NULL; it's OK to send a packet when listening
     138    // because there's no reply; OTOH, you can't do an RPC while listening because
     139    // an unsolicited packet might get mixed up with the RPC reply.
     140   
     141    assert(packet != NULL);
     142    assert(packet->fMagic == kPacketMagic);
     143    assert(packet->fSize >= sizeof(PacketHeader));
     144   
     145    // Simply send the packet down the socket.
     146   
     147    err = MoreUNIXWrite(conn->fSockFD, packet, packet->fSize, NULL);
     148   
     149    return err;
     150}
     151
     152static int ConnectionOpen(ConnectionRef *connPtr)
     153// Opens a connection to the server.
     154//
     155// On entry, connPtr must not be NULL
     156// On entry, *connPtr must be NULL
     157// Returns an errno-style error code
     158// On success, *connPtr will not be NULL
     159// On error, *connPtr will be NULL
     160{
     161    int                 err;
     162    ConnectionRef       conn;
     163    Boolean             sayGoodbye;
     164   
     165    assert( connPtr != NULL);
     166    assert(*connPtr == NULL);
     167   
     168    sayGoodbye = false;
     169   
     170    // Allocate a ConnectionState structure and fill out some basic fields.
     171   
     172    err = 0;
     173    conn = (ConnectionRef) calloc(1, sizeof(*conn));
     174    if (conn == NULL) {
     175        err = ENOMEM;
     176    }
     177    if (err == 0) {
     178        conn->fMagic  = kConnectionStateMagic;
     179       
     180        // For clean up to work properly, we must make sure that, if
     181        // the connection record is allocated successfully, we always
     182        // set fSockFD to -1.  So, while the following line is redundant
     183        // in the current code, it's present to press home this point.
     184               
     185        conn->fSockFD = -1;
     186    }
     187   
     188    // Create a UNIX domain socket and connect to the server.
     189   
     190    if (err == 0) {
     191        conn->fSockFD = socket(AF_UNIX, SOCK_STREAM, 0);
     192        err = MoreUNIXErrno(conn->fSockFD);
     193    }
     194    if (err == 0) {
     195        struct sockaddr_un connReq;
     196               
     197        connReq.sun_len    = sizeof(connReq);
     198        connReq.sun_family = AF_UNIX;
     199        strcpy(connReq.sun_path, kServerSocketPath);
     200               
     201        err = connect(conn->fSockFD, (struct sockaddr *) &connReq, SUN_LEN(&connReq));
     202        err = MoreUNIXErrno(err);
     203       
     204        sayGoodbye = (err == 0);
     205    }
     206   
     207    // Clean up.
     208   
     209    if (err != 0) {
     210        ConnectionCloseInternal(conn, sayGoodbye);
     211        conn = NULL;
     212    }
     213    *connPtr = conn;
     214   
     215    assert( (err == 0) == (*connPtr != NULL) );
     216   
     217    return err;
     218}
     219
     220
     221static void ConnectionShutdown(ConnectionRef conn)
     222// This routine shuts down down the connection to the server
     223// without saying goodbye; it leaves conn valid.  This routine
     224// is primarily used internally to the connection abstraction
     225// where we notice that the connection has failed for some reason.
     226// It's also called by the client after a successful quit RPC
     227// because we know that the server has closed its end of the
     228// connection.
     229//
     230// It's important to nil out the fields as we close them because
     231// this routine is called if any messaging routine fails.  If it
     232// doesn't nil out the fields, two bad things might happen:
     233//
     234// o When the connection is eventually closed, ConnectionCloseInternal
     235//   will try to send a Goodbye, which fails triggering an assert.
     236//
     237// o If ConnectionShutdown is called twice on a particular connection
     238//   (which happens a lot; this is a belts and braces implementation
     239//   [that's "belts and suspenders" for the Americans reading this;
     240//   ever wonder why Monty Python's lumberjacks sing about "suspenders
     241//   and a bra"?; well look up "suspenders" in a non-American dictionary
     242//   for a quiet chuckle :-] )
     243{
     244    int     junk;
     245    Boolean hadSockCF;
     246       
     247    assert(conn != NULL);
     248   
     249    conn->fCallback       = NULL;
     250    conn->fCallbackRefCon = NULL;
     251       
     252    if (conn->fRunLoopSource != NULL) {
     253        CFRunLoopSourceInvalidate(conn->fRunLoopSource);
     254       
     255        CFRelease(conn->fRunLoopSource);
     256       
     257        conn->fRunLoopSource = NULL;
     258    }
     259   
     260    // CFSocket will close conn->fSockFD when we invalidate conn->fSockCF,
     261    // so we remember whether we did this so that, later on, we know
     262    // whether to close the file descriptor ourselves.  We need an extra
     263    // variable because we NULL out fSockCF as we release it, for the reason
     264    // described above.
     265   
     266    hadSockCF = (conn->fSockCF != NULL);
     267    if (conn->fSockCF != NULL) {
     268        CFSocketInvalidate(conn->fSockCF);
     269       
     270        CFRelease(conn->fSockCF);
     271       
     272        conn->fSockCF = NULL;
     273    }
     274       
     275    if (conn->fBufferedPackets != NULL) {
     276        CFRelease(conn->fBufferedPackets);
     277        conn->fBufferedPackets = NULL;
     278    }
     279       
     280    if ( (conn->fSockFD != -1) && ! hadSockCF ) {
     281        junk = close(conn->fSockFD);
     282        assert(junk == 0);
     283    }
     284    // We always set fSockFD to -1 because either we've closed it or
     285    // CFSocket has.
     286    conn->fSockFD = -1;
     287}
     288
     289
     290static void ConnectionCloseInternal(ConnectionRef conn, Boolean sayGoodbye)
     291// The core of ConnectionClose.  It's called by ConnectionClose
     292// and by ConnectionOpen, if it fails for some reason.  This exists
     293// as a separate routine so that we can add the sayGoodbye parameter,
     294// which controls whether we send a goodbye packet to the server.  We
     295// need this because we should always try to say goodbye if we're called
     296// from ConnectionClose, but if we're called from ConnectionOpen we
     297// should only try to say goodbye if we successfully connected the
     298// socket.
     299//
     300// Regardless, the bulk of the work of this routine is done by
     301// ConnectionShutdown.  This routine exists to a) say goodbye, if
     302// necessary, and b) free the memory associated with the connection.
     303{
     304    int     junk;
     305   
     306    if (conn != NULL) {
     307        assert(conn->fMagic == kConnectionStateMagic);
     308               
     309        if ( (conn->fSockFD != -1) && sayGoodbye ) {
     310            PacketGoodbye   goodbye;
     311                       
     312            InitPacketHeader(&goodbye.fHeader, kPacketTypeGoodbye, sizeof(goodbye), false);
     313            snprintf(goodbye.fMessage, sizeof(goodbye.fMessage), "Process %ld signing off", (long) getpid());
     314           
     315            junk = ConnectionSend(conn, &goodbye.fHeader);
     316            assert(junk == 0);
     317        }
     318        ConnectionShutdown(conn);
     319       
     320        free(conn);
     321    }
     322}
     323
     324static void ConnectionClose(ConnectionRef conn)
     325// Closes the connection.  It's legal to pass conn as NULL, in which
     326// case this does nothing (kinda like free'ing NULL).
     327{
     328    ConnectionCloseInternal(conn, true);
     329}
     330
     331
     332
     333/////////////////////////////////////////////////////////////////
     334#pragma mark ***** Notifications Command Utilities
     335
     336static void SIGINTRunLoopCallback(const siginfo_t *sigInfo, void *refCon)
     337// This routine is called in response to a SIGINT signal.
     338// It is not, however, a signal handler.  Rather, we
     339// orchestrate to have it called from the runloop (via
     340// the magic of InstallSignalToSocket).  It's purpose
     341// is to stop the runloop when the user types ^C.
     342{
     343#pragma unused(sigInfo)
     344#pragma unused(refCon)
     345   
     346    // Stop the runloop.  Note that we can get a reference to the runloop by
     347        // calling CFRunLoopGetCurrent because this is called from the runloop.
     348   
     349    CFRunLoopStop( CFRunLoopGetCurrent() );
     350   
     351    // Print a bonus newline to ensure that the next command prompt isn't
     352    // printed on the same line as the echoed ^C.
     353   
     354    fprintf(stderr, "\n");
     355}
     356
     357
     358static void PrintResult(const char *command, int errNum, const char *arg)
     359// Prints the result of a command.  command is the name of the
     360// command, errNum is the errno-style error number, and arg
     361// (if not NULL) is the command argument.
     362{
     363    if (errNum == 0) {
     364        if (arg == NULL) {
     365            fprintf(stderr, "%*s\n", kResultColumnWidth, command);
     366        } else {
     367            fprintf(stderr, "%*s \"%s\"\n", kResultColumnWidth, command, arg);
     368        }
     369    } else {
     370        fprintf(stderr, "%*s failed with error %d\n", kResultColumnWidth, command, errNum);
     371    }
     372}
     373
     374
     375
     376
     377static void DoShout(ConnectionRef conn, const char *message)
     378// Implements the "shout" command by sending a shout packet to the server.
     379// Note that this is /not/ an RPC.
     380//
     381// The server responds to this packet by echoing it to each registered
     382// listener.
     383{
     384       
     385        //asl logging stuff
     386        aslmsg logMsg = asl_new(ASL_TYPE_MSG) ;
     387        assert(logMsg != NULL);
     388        asl_set(logMsg, ASL_KEY_FACILITY, "com.apple.console");
     389        asl_set(logMsg, ASL_KEY_SENDER, "MPHelperTool");
     390       
     391        aslclient logClient = asl_open(NULL , NULL, ASL_OPT_STDERR);
     392        assert(logClient != NULL);
     393
     394       
     395    int         err;
     396        NSString * shoutString = [NSString stringWithFormat:@"Do Shout is passing %@",
     397                                                          [NSString stringWithCString:message encoding:NSUTF8StringEncoding]];
     398        err = asl_NSLog(logClient , logMsg, ASL_LEVEL_DEBUG, @" %@", shoutString );
     399       
     400       
     401       
     402    PacketShout request;   
     403    InitPacketHeader(&request.fHeader, kPacketTypeShout, sizeof(request), false);
     404    snprintf(request.fMessage, sizeof(request.fMessage), "%s", message);
     405    err = ConnectionSend(conn, &request.fHeader);
     406    PrintResult("shout", err, message);
     407       
     408        asl_close(logClient);
     409}
     410
     411# pragma mark -
    53412
    54413
     
    97456
    98457
     458
    99459#pragma mark -
     460
     461
     462
     463
     464
     465/////////////////////////////////////////////////////////////////
     466#pragma mark ***** Tool Infrastructure
     467
     468/*
     469 IMPORTANT
     470 ---------
     471 This array must be exactly parallel to the kMPHelperCommandSet array
     472 in "MPHelperCommon.c".
     473 */
    100474
    101475static OSStatus DoEvaluateTclString
     
    172546        else
    173547                CFDictionaryAddValue(response, CFSTR("TclPkgPath"), (CFStringRef)tclPkgPath);
    174                
     548       
    175549        //Load macports1.0 package
    176550        NSString * mport_fastload = [[@"source [file join \"" stringByAppendingString:tclPkgPath]
     
    188562        }
    189563       
    190 
     564       
    191565        //Add simplelog tcl command
    192566        Tcl_CreateObjCommand(interpreter, "simplelog", SimpleLog_Command, NULL, NULL);
     
    200574                CFDictionaryAddValue(response, CFSTR("simplelog"), CFSTR("YES"));
    201575        }
    202                
     576       
    203577       
    204578        //Get path for and load interpInit.tcl file to Tcl Interpreter
     
    251625}
    252626
    253 /////////////////////////////////////////////////////////////////
    254 #pragma mark ***** Tool Infrastructure
    255 
    256 /*
    257  IMPORTANT
    258  ---------
    259  This array must be exactly parallel to the kMPHelperCommandSet array
    260  in "MPHelperCommon.c".
    261  */
    262627
    263628static const BASCommandProc kMPHelperCommandProcs[] = {
     
    269634        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    270635       
     636       
     637       
     638        //For Debugging
     639        //asl logging stuff
     640        aslmsg logMsg = asl_new(ASL_TYPE_MSG) ;
     641        assert(logMsg != NULL);
     642        asl_set(logMsg, ASL_KEY_FACILITY, "com.apple.console");
     643        asl_set(logMsg, ASL_KEY_SENDER, "MPHelperTool");
     644        aslclient logClient = asl_open(NULL , NULL, ASL_OPT_STDERR);
     645        assert(logClient != NULL);
     646       
     647       
     648        int             err;
     649    ConnectionRef   conn;
     650    conn = NULL;
     651   
     652    // SIGPIPE is evil, so tell the system not to send it to us.
     653    if (err == 0) {
     654        err = MoreUNIXIgnoreSIGPIPE();
     655    }
     656       
     657    // Organise to have SIGINT delivered to a runloop callback.
     658    if (err == 0) {
     659        sigset_t    justSIGINT;
     660       
     661        (void) sigemptyset(&justSIGINT);
     662        (void) sigaddset(&justSIGINT, SIGINT);
     663       
     664        err = InstallSignalToSocket(
     665                                                                        &justSIGINT,
     666                                                                        CFRunLoopGetCurrent(),
     667                                                                        kCFRunLoopDefaultMode,
     668                                                                        SIGINTRunLoopCallback,
     669                                                                        NULL
     670                                                                        );
     671    }
     672   
     673    // Connect to the server.
     674    if (err == 0) {
     675        err = ConnectionOpen(&conn);
     676    }
     677   
     678    // Process the command line arguments.  Basically the arguments are a
     679    // sequence of commands, which we process in order.  The logic is
     680    // a little convoluted because some commands have arguments and because
     681    // the "listen" command must come last.
     682   
     683    if (err == 0) {
     684                asl_NSLog(logClient , logMsg, ASL_LEVEL_DEBUG, @"MPHelperTool: calling DoShout");
     685                DoShout(conn, "Testing MPHelperTool IPC");
     686        }
     687        else
     688                asl_NSLog(logClient , logMsg, ASL_LEVEL_DEBUG, @"MPHelperTool: calling DoShout");
     689        asl_close(logClient);
     690       
     691    // Clean up.
     692    ConnectionClose(conn);
     693       
     694       
    271695        int result = BASHelperToolMain(kMPHelperCommandSet, kMPHelperCommandProcs);
    272696       
  • branches/gsoc08-framework/MacPorts_Framework_Release/MacPorts.Framework.xcodeproj/project.pbxproj

    r39471 r39514  
    4545                6E49F37B0DFFAB0B0030C3AF /* MPInterpreterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 489DD92E0C94674B00595506 /* MPInterpreterTest.m */; };
    4646                6E49F37F0DFFAFF80030C3AF /* MacPorts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* MacPorts.framework */; };
     47                6E8563B80E5DDF7000C1D73C /* MPHelperNotificationsCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 6E8563B50E5DDF7000C1D73C /* MPHelperNotificationsCommon.c */; };
     48                6E8563B90E5DDF7000C1D73C /* MPHelperNotificationsCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E8563B60E5DDF7000C1D73C /* MPHelperNotificationsCommon.h */; };
     49                6E8563BA0E5DDF7000C1D73C /* MPHelperNotificationsProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E8563B70E5DDF7000C1D73C /* MPHelperNotificationsProtocol.h */; };
     50                6E8563BB0E5DDF7000C1D73C /* MPHelperNotificationsCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 6E8563B50E5DDF7000C1D73C /* MPHelperNotificationsCommon.c */; };
     51                6E8563BC0E5DDF7000C1D73C /* MPHelperNotificationsCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E8563B60E5DDF7000C1D73C /* MPHelperNotificationsCommon.h */; };
     52                6E8563BD0E5DDF7000C1D73C /* MPHelperNotificationsProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E8563B70E5DDF7000C1D73C /* MPHelperNotificationsProtocol.h */; };
    4753                6EA294590E080DEB00902D12 /* MPMacPortsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E88D1CB0DF4B90B00684E9F /* MPMacPortsTest.m */; };
    4854                6EB6FA420E43ECCE0057962C /* MPHelperTool in Resources */ = {isa = PBXBuildFile; fileRef = 6ED12A4A0E3E552F0026773D /* MPHelperTool */; };
     
    129135                6E3345340E54AF14008A2F6C /* MPPortManipulationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPortManipulationTest.m; sourceTree = "<group>"; };
    130136                6E44A00C0E2DAD66007DE8EC /* ToDo.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ToDo.txt; sourceTree = "<group>"; };
     137                6E8563B50E5DDF7000C1D73C /* MPHelperNotificationsCommon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = MPHelperNotificationsCommon.c; sourceTree = "<group>"; };
     138                6E8563B60E5DDF7000C1D73C /* MPHelperNotificationsCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPHelperNotificationsCommon.h; sourceTree = "<group>"; };
     139                6E8563B70E5DDF7000C1D73C /* MPHelperNotificationsProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPHelperNotificationsProtocol.h; sourceTree = "<group>"; };
    131140                6E88D1CA0DF4B90B00684E9F /* MPMacPortsTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacPortsTest.h; sourceTree = "<group>"; };
    132141                6E88D1CB0DF4B90B00684E9F /* MPMacPortsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMacPortsTest.m; sourceTree = "<group>"; };
     
    329338                        isa = PBXGroup;
    330339                        children = (
     340                                6E8563B50E5DDF7000C1D73C /* MPHelperNotificationsCommon.c */,
     341                                6E8563B60E5DDF7000C1D73C /* MPHelperNotificationsCommon.h */,
     342                                6E8563B70E5DDF7000C1D73C /* MPHelperNotificationsProtocol.h */,
    331343                                6E270D070E158CED00BAE687 /* MPNotifications.h */,
    332344                                6E270D080E158CED00BAE687 /* MPNotifications.m */,
     
    357369                                6EC260740E426FC80013BC48 /* BetterAuthorizationSampleLib.h in Headers */,
    358370                                6EC260970E4272D20013BC48 /* MPHelperCommon.h in Headers */,
     371                                6E8563BC0E5DDF7000C1D73C /* MPHelperNotificationsCommon.h in Headers */,
     372                                6E8563BD0E5DDF7000C1D73C /* MPHelperNotificationsProtocol.h in Headers */,
    359373                        );
    360374                        runOnlyForDeploymentPostprocessing = 0;
     
    375389                                6EC260770E426FC80013BC48 /* BetterAuthorizationSampleLib.h in Headers */,
    376390                                6EC260990E4272D20013BC48 /* MPHelperCommon.h in Headers */,
     391                                6E8563B90E5DDF7000C1D73C /* MPHelperNotificationsCommon.h in Headers */,
     392                                6E8563BA0E5DDF7000C1D73C /* MPHelperNotificationsProtocol.h in Headers */,
    377393                        );
    378394                        runOnlyForDeploymentPostprocessing = 0;
     
    582598                                6EC260980E4272D20013BC48 /* MPHelperCommon.c in Sources */,
    583599                                6ECD98110E484E8400488335 /* MPHelperTool.m in Sources */,
     600                                6E8563BB0E5DDF7000C1D73C /* MPHelperNotificationsCommon.c in Sources */,
    584601                        );
    585602                        runOnlyForDeploymentPostprocessing = 0;
     
    599616                                6EC260760E426FC80013BC48 /* BetterAuthorizationSampleLib.c in Sources */,
    600617                                6EC2609A0E4272D20013BC48 /* MPHelperCommon.c in Sources */,
     618                                6E8563B80E5DDF7000C1D73C /* MPHelperNotificationsCommon.c in Sources */,
    601619                        );
    602620                        runOnlyForDeploymentPostprocessing = 0;
Note: See TracChangeset for help on using the changeset viewer.