コード例 #1
0
bool R3DSurfaceGenProcess::runSurfaceGenProcess(R3DProject::Surface *pSurface)
{
	R3DProject *pProject = R3DProject::getInstance();
	R3DProjectPaths paths;
	if(!pProject->getProjectPathsSrf(paths, pSurface))
		return false;
	pSurface_ = pSurface;

	beginTime_ = wxDateTime::UNow();


#if wxCHECK_VERSION(2, 9, 0)
	env_.cwd = paths.absoluteProjectPath_;

	// Set PATH/LD_LIBRARY_PATH/DYLD_LIBRARY_PATH environment variable
#if defined(R3D_WIN32)
	wxString envVarName(wxT("PATH"));
#elif defined(R3D_MACOSX)
	wxString envVarName(wxT("DYLD_LIBRARY_PATH"));
#else
	wxString envVarName(wxT("LD_LIBRARY_PATH"));
#endif

	wxEnvVariableHashMap envVars;
	if(wxGetEnvMap(&envVars))
	{
		wxString concatPaths;
		const wxArrayString &exePaths = R3DExternalPrograms::getInstance().getAllPaths();
		for(size_t i = 0; i < exePaths.GetCount(); i++)
		{
			const wxString &curPath = exePaths[i];

			if(!concatPaths.IsEmpty())
#if defined(R3D_WIN32)
				concatPaths.Append(wxT(";"));
#else
				concatPaths.Append(wxT(":"));
#endif

			concatPaths.Append(curPath);
		}
		wxEnvVariableHashMap::iterator iter = envVars.find(envVarName);
		if(iter != envVars.end())
		{
			wxString val = iter->second;
			if(!val.IsEmpty())
#if defined(R3D_WIN32)
				val.Append(wxT(";"));
#else
				val.Append(wxT(":"));
#endif
			val.Append(concatPaths);
			envVars[envVarName] = val;
		}
		else
		{
			envVars[envVarName] = concatPaths;
		}
		env_.env = envVars;
	}
#endif

	cmds_.Clear();
	progressTexts_.Clear();

	wxString relativeSurfacePath(paths.relativeSurfacePath_.c_str(), wxConvLibc);
	wxString relativeSurfaceFilename;		// Used for texturing

	if(pSurface->surfaceType_ == R3DProject::STPoissonRecon)
	{
		wxString poissonReconExe = R3DExternalPrograms::getInstance().getPoissonReconPath();
		wxString surfaceTrimmerExe = R3DExternalPrograms::getInstance().getSurfaceTrimmerPath();
		wxString texreconExe = R3DExternalPrograms::getInstance().getTexReconPath();

		wxString poissonReconCmd = poissonReconExe + wxT(" --in ") + wxString(paths.relativeDenseModelName_.c_str(), wxConvLibc)
			+ wxT(" --out ") + relativeSurfacePath + wxT("/model_surface.ply")
			+ wxString::Format(wxT(" --depth %d --pointWeight %g --samplesPerNode %g"), 
			pSurface->poissonDepth_, pSurface->poissonPointWeight_, pSurface->poissonSamplesPerNode_);
		if(pSurface->poissonTrimThreshold_ > 0)
			poissonReconCmd.Append(wxT(" --density"));
		poissonReconCmd.Append(wxT(" --verbose"));
		cmds_.Add(poissonReconCmd);
		progressTexts_.Add(wxT("Generating surface (PoissonRecon)"));
		wxString surfaceFilename(wxT("model_surface.ply"));

		if(pSurface->poissonTrimThreshold_ > 0)
		{
			cmds_.Add(surfaceTrimmerExe + wxT(" --in ") + relativeSurfacePath + wxT("/model_surface.ply ") 
				+ wxString::Format(wxT("--trim %g"), pSurface->poissonTrimThreshold_)
				+ wxT(" --out ") + relativeSurfacePath + wxT("/model_surface_trim.ply"));
			progressTexts_.Add(wxT("Trimming surface"));
			surfaceFilename = wxT("model_surface_trim.ply");
		}

		// Store for later, will either be overwritten (texture case) or used as input (colorizing vertices case)
		pSurface->finalSurfaceFilename_ = surfaceFilename;

		if(pSurface->colorizationType_ == R3DProject::CTTextures)
		{
			wxFileName surfaceFN(relativeSurfacePath, surfaceFilename);
			relativeSurfaceFilename = surfaceFN.GetFullPath();

			pSurface->finalSurfaceFilename_ = wxT("model_surface_text.obj");
		}
	}
	else if(pSurface->surfaceType_ == R3DProject::STFSSRecon)
	{
		wxString fssReconExe = R3DExternalPrograms::getInstance().getFSSReconPath();
		wxString meshcleanExe = R3DExternalPrograms::getInstance().getMeshCleanPath();

		wxString surfaceModelName(wxT("surface_model.ply"));
		wxString cleanedSurfaceModelName(wxT("surface_clean_model.ply"));
		wxFileName surfaceModelFN(wxString(paths.relativeSurfacePath_.c_str(), wxConvLibc), surfaceModelName);
		wxFileName cleanedSurfaceModelFN(wxString(paths.relativeSurfacePath_.c_str(), wxConvLibc), cleanedSurfaceModelName);

		cmds_.Add(fssReconExe + wxString::Format(wxT(" --scale-factor=%g --refine-octree=%d "),
			pSurface->fssrScaleFactorMultiplier_, pSurface->fssrRefineOctreeLevels_)
			+ wxString(paths.relativeDenseModelName_.c_str(), wxConvLibc)
			+ wxT(" ")
			+ surfaceModelFN.GetFullPath());
		progressTexts_.Add(wxT("Generating surface (FSSR)"));

		cmds_.Add(meshcleanExe + wxString::Format(wxT(" --threshold=%g --component-size=%d "),
			pSurface->fssrConfidenceThreshold_, pSurface->fssrMinComponentSize_)
			+ surfaceModelFN.GetFullPath()
			+ wxT(" ")
			+ cleanedSurfaceModelFN.GetFullPath());
		progressTexts_.Add(wxT("Cleaning mesh (FSSR)"));

		relativeSurfaceFilename = cleanedSurfaceModelFN.GetFullPath();

		// Used only in case of colored vertices
		pSurface->finalSurfaceFilename_ = cleanedSurfaceModelName;
	}

	if(pSurface->colorizationType_ == R3DProject::CTTextures)
	{
		wxString texreconExe = R3DExternalPrograms::getInstance().getTexReconPath();

		wxString texreconCmd(texreconExe + wxT(" "));
		if(pSurface->textGeometricVisibilityTest_ == false)
			texreconCmd += wxT("--skip_geometric_visibility_test ");
		if(pSurface->textGlobalSeamLeveling_ == false)
			texreconCmd += wxT("--skip_global_seam_leveling ");
		if(pSurface->textLocalSeamLeveling_ == false)
			texreconCmd += wxT("--skip_local_seam_leveling ");
		if(pSurface->textOutlierRemovalType_ == 1)
			texreconCmd += wxT("--outlier_removal=gauss_clamping ");
		if(pSurface->textOutlierRemovalType_ == 2)
			texreconCmd += wxT("--outlier_removal=gauss_damping ");
		texreconCmd += wxT("--no_intermediate_results ");
		texreconCmd += wxString(paths.relativeMVESceneDir_.c_str(), wxConvLibc)
			+ wxT("::undistorted ")
			+ relativeSurfaceFilename + wxT(" ")
			+ relativeSurfacePath + wxT("/model_surface_text");

		cmds_.Add(texreconCmd);
		progressTexts_.Add(wxT("Generating texture (texrecon)"));

		pSurface->finalSurfaceFilename_ = wxT("model_surface_text.obj");
	}
	else if(pSurface->colorizationType_ == R3DProject::CTColoredVertices)
	{
		// No external program, this case is handled in Regard3DMainFrame::OnSurfaceGenFinished
	}

	runSingleCommand();

	return (processId_ > 0);
}
コード例 #2
0
ファイル: utilsunx.cpp プロジェクト: Asmodean-/Ishiiruka
// wxExecute: the real worker function
long wxExecute(char **argv, int flags, wxProcess *process,
        const wxExecuteEnv *env)
{
    // for the sync execution, we return -1 to indicate failure, but for async
    // case we return 0 which is never a valid PID
    //
    // we define this as a macro, not a variable, to avoid compiler warnings
    // about "ERROR_RETURN_CODE value may be clobbered by fork()"
    #define ERROR_RETURN_CODE ((flags & wxEXEC_SYNC) ? -1 : 0)

    wxCHECK_MSG( *argv, ERROR_RETURN_CODE, wxT("can't exec empty command") );

#if wxUSE_THREADS
    // fork() doesn't mix well with POSIX threads: on many systems the program
    // deadlocks or crashes for some reason. Probably our code is buggy and
    // doesn't do something which must be done to allow this to work, but I
    // don't know what yet, so for now just warn the user (this is the least we
    // can do) about it
    wxASSERT_MSG( wxThread::IsMain(),
                    wxT("wxExecute() can be called only from the main thread") );
#endif // wxUSE_THREADS

#if defined(__DARWIN__) && !defined(__WXOSX_IPHONE__)
    // wxMacLaunch() only executes app bundles and only does it asynchronously.
    // It returns false if the target is not an app bundle, thus falling
    // through to the regular code for non app bundles.
    if ( !(flags & wxEXEC_SYNC) && wxMacLaunch(argv) )
    {
        // we don't have any PID to return so just make up something non null
        return -1;
    }
#endif // __DARWIN__

    // this struct contains all information which we use for housekeeping
    wxScopedPtr<wxExecuteData> execDataPtr(new wxExecuteData);
    wxExecuteData& execData = *execDataPtr;

    execData.flags = flags;
    execData.process = process;

    // create pipes for inter process communication
    wxPipe pipeIn,      // stdin
           pipeOut,     // stdout
           pipeErr;     // stderr

    if ( process && process->IsRedirected() )
    {
        if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() )
        {
            wxLogError( _("Failed to execute '%s'\n"), *argv );

            return ERROR_RETURN_CODE;
        }
    }

    // priority: we need to map wxWidgets priority which is in the range 0..100
    // to Unix nice value which is in the range -20..19. As there is an odd
    // number of elements in our range and an even number in the Unix one, we
    // have to do it in this rather ugly way to guarantee that:
    //  1. wxPRIORITY_{MIN,DEFAULT,MAX} map to -20, 0 and 19 respectively.
    //  2. The mapping is monotonously increasing.
    //  3. The mapping is onto the target range.
    int prio = process ? int(process->GetPriority()) : int(wxPRIORITY_DEFAULT);
    if ( prio <= 50 )
        prio = (2*prio)/5 - 20;
    else if ( prio < 55 )
        prio = 1;
    else
        prio = (2*prio)/5 - 21;

    // fork the process
    //
    // NB: do *not* use vfork() here, it completely breaks this code for some
    //     reason under Solaris (and maybe others, although not under Linux)
    //     But on OpenVMS we do not have fork so we have to use vfork and
    //     cross our fingers that it works.
#ifdef __VMS
   pid_t pid = vfork();
#else
   pid_t pid = fork();
#endif
   if ( pid == -1 )     // error?
    {
        wxLogSysError( _("Fork failed") );

        return ERROR_RETURN_CODE;
    }
    else if ( pid == 0 )  // we're in child
    {
        // NB: we used to close all the unused descriptors of the child here
        //     but this broke some programs which relied on e.g. FD 1 being
        //     always opened so don't do it any more, after all there doesn't
        //     seem to be any real problem with keeping them opened

#if !defined(__VMS)
        if ( flags & wxEXEC_MAKE_GROUP_LEADER )
        {
            // Set process group to child process' pid.  Then killing -pid
            // of the parent will kill the process and all of its children.
            setsid();
        }
#endif // !__VMS

#if defined(HAVE_SETPRIORITY)
        if ( prio && setpriority(PRIO_PROCESS, 0, prio) != 0 )
        {
            wxLogSysError(_("Failed to set process priority"));
        }
#endif // HAVE_SETPRIORITY

        // redirect stdin, stdout and stderr
        if ( pipeIn.IsOk() )
        {
            if ( dup2(pipeIn[wxPipe::Read], STDIN_FILENO) == -1 ||
                 dup2(pipeOut[wxPipe::Write], STDOUT_FILENO) == -1 ||
                 dup2(pipeErr[wxPipe::Write], STDERR_FILENO) == -1 )
            {
                wxLogSysError(_("Failed to redirect child process input/output"));
            }

            pipeIn.Close();
            pipeOut.Close();
            pipeErr.Close();
        }

        // Close all (presumably accidentally) inherited file descriptors to
        // avoid descriptor leaks. This means that we don't allow inheriting
        // them purposefully but this seems like a lesser evil in wx code.
        // Ideally we'd provide some flag to indicate that none (or some?) of
        // the descriptors do not need to be closed but for now this is better
        // than never closing them at all as wx code never used FD_CLOEXEC.

        // TODO: Iterating up to FD_SETSIZE is both inefficient (because it may
        //       be quite big) and incorrect (because in principle we could
        //       have more opened descriptions than this number). Unfortunately
        //       there is no good portable solution for closing all descriptors
        //       above a certain threshold but non-portable solutions exist for
        //       most platforms, see [http://stackoverflow.com/questions/899038/
        //          getting-the-highest-allocated-file-descriptor]
        for ( int fd = 0; fd < (int)FD_SETSIZE; ++fd )
        {
            if ( fd != STDIN_FILENO  &&
                 fd != STDOUT_FILENO &&
                 fd != STDERR_FILENO )
            {
                close(fd);
            }
        }


        // Process additional options if we have any
        if ( env )
        {
            // Change working directory if it is specified
            if ( !env->cwd.empty() )
                wxSetWorkingDirectory(env->cwd);

            // Change environment if needed.
            //
            // NB: We can't use execve() currently because we allow using
            //     non full paths to wxExecute(), i.e. we want to search for
            //     the program in PATH. However it just might be simpler/better
            //     to do the search manually and use execve() envp parameter to
            //     set up the environment of the child process explicitly
            //     instead of doing what we do below.
            if ( !env->env.empty() )
            {
                wxEnvVariableHashMap oldenv;
                wxGetEnvMap(&oldenv);

                // Remove unwanted variables
                wxEnvVariableHashMap::const_iterator it;
                for ( it = oldenv.begin(); it != oldenv.end(); ++it )
                {
                    if ( env->env.find(it->first) == env->env.end() )
                        wxUnsetEnv(it->first);
                }

                // And add the new ones (possibly replacing the old values)
                for ( it = env->env.begin(); it != env->env.end(); ++it )
                    wxSetEnv(it->first, it->second);
            }
        }

        execvp(*argv, argv);

        fprintf(stderr, "execvp(");
        for ( char **a = argv; *a; a++ )
            fprintf(stderr, "%s%s", a == argv ? "" : ", ", *a);
        fprintf(stderr, ") failed with error %d!\n", errno);

        // there is no return after successful exec()
        _exit(-1);

        // some compilers complain about missing return - of course, they
        // should know that exit() doesn't return but what else can we do if
        // they don't?
        //
        // and, sure enough, other compilers complain about unreachable code
        // after exit() call, so we can just always have return here...
#if defined(__VMS) || defined(__INTEL_COMPILER)
        return 0;
#endif
    }
    else // we're in parent
    {
        // prepare for IO redirection

#if HAS_PIPE_STREAMS

        if ( process && process->IsRedirected() )
        {
            // Avoid deadlocks which could result from trying to write to the
            // child input pipe end while the child itself is writing to its
            // output end and waiting for us to read from it.
            if ( !pipeIn.MakeNonBlocking(wxPipe::Write) )
            {
                // This message is not terrible useful for the user but what
                // else can we do? Also, should we fail here or take the risk
                // to continue and deadlock? Currently we choose the latter but
                // it might not be the best idea.
                wxLogSysError(_("Failed to set up non-blocking pipe, "
                                "the program might hang."));
#if wxUSE_LOG
                wxLog::FlushActive();
#endif
            }

            wxOutputStream *inStream =
                new wxPipeOutputStream(pipeIn.Detach(wxPipe::Write));

            const int fdOut = pipeOut.Detach(wxPipe::Read);
            wxPipeInputStream *outStream = new wxPipeInputStream(fdOut);

            const int fdErr = pipeErr.Detach(wxPipe::Read);
            wxPipeInputStream *errStream = new wxPipeInputStream(fdErr);

            process->SetPipeStreams(outStream, inStream, errStream);

            if ( flags & wxEXEC_SYNC )
            {
                execData.bufOut.Init(outStream);
                execData.bufErr.Init(errStream);

                execData.fdOut = fdOut;
                execData.fdErr = fdErr;
            }
        }
#endif // HAS_PIPE_STREAMS

        if ( pipeIn.IsOk() )
        {
            pipeIn.Close();
            pipeOut.Close();
            pipeErr.Close();
        }

        if ( !(flags & wxEXEC_SYNC) )
        {
            // Ensure that the housekeeping data is kept alive, it will be
            // destroyed only when the child terminates.
            execDataPtr.release();
        }

        // Put the housekeeping data into the child process lookup table.
        // Note that when running asynchronously, if the child has already
        // finished this call will delete the execData and call any
        // wxProcess's OnTerminate() handler immediately.
        execData.OnStart(pid);

        // For the asynchronous case we don't have to do anything else, just
        // let the process run (if not already finished).
        if ( !(flags & wxEXEC_SYNC) )
            return pid;


        // If we don't need to dispatch any events, things are relatively
        // simple and we don't need to delegate to wxAppTraits.
        if ( flags & wxEXEC_NOEVENTS )
        {
            return BlockUntilChildExit(execData);
        }


        // If we do need to dispatch events, enter a local event loop waiting
        // until the child exits. As the exact kind of event loop depends on
        // the sort of application we're in (console or GUI), we delegate this
        // to wxAppTraits which virtualizes all the differences between the
        // console and the GUI programs.
        return wxApp::GetValidTraits().WaitForChild(execData);
    }

#if !defined(__VMS) && !defined(__INTEL_COMPILER)
    return ERROR_RETURN_CODE;
#endif
}
コード例 #3
0
ファイル: utilsunx.cpp プロジェクト: iokto/newton-dynamics
// wxExecute: the real worker function
long wxExecute(char **argv, int flags, wxProcess *process,
        const wxExecuteEnv *env)
{
    // for the sync execution, we return -1 to indicate failure, but for async
    // case we return 0 which is never a valid PID
    //
    // we define this as a macro, not a variable, to avoid compiler warnings
    // about "ERROR_RETURN_CODE value may be clobbered by fork()"
    #define ERROR_RETURN_CODE ((flags & wxEXEC_SYNC) ? -1 : 0)

    wxCHECK_MSG( *argv, ERROR_RETURN_CODE, wxT("can't exec empty command") );

#if wxUSE_THREADS
    // fork() doesn't mix well with POSIX threads: on many systems the program
    // deadlocks or crashes for some reason. Probably our code is buggy and
    // doesn't do something which must be done to allow this to work, but I
    // don't know what yet, so for now just warn the user (this is the least we
    // can do) about it
    wxASSERT_MSG( wxThread::IsMain(),
                    wxT("wxExecute() can be called only from the main thread") );
#endif // wxUSE_THREADS

#if defined(__WXCOCOA__) || ( defined(__WXOSX_MAC__) && wxOSX_USE_COCOA_OR_CARBON )
    // wxMacLaunch() only executes app bundles and only does it asynchronously.
    // It returns false if the target is not an app bundle, thus falling
    // through to the regular code for non app bundles.
    if ( !(flags & wxEXEC_SYNC) && wxMacLaunch(argv) )
    {
        // we don't have any PID to return so just make up something non null
        return -1;
    }
#endif // __DARWIN__


    // this struct contains all information which we use for housekeeping
    wxExecuteData execData;
    execData.flags = flags;
    execData.process = process;

    // create pipes
    if ( !execData.pipeEndProcDetect.Create() )
    {
        wxLogError( _("Failed to execute '%s'\n"), *argv );

        return ERROR_RETURN_CODE;
    }

    // pipes for inter process communication
    wxPipe pipeIn,      // stdin
           pipeOut,     // stdout
           pipeErr;     // stderr

    if ( process && process->IsRedirected() )
    {
        if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() )
        {
            wxLogError( _("Failed to execute '%s'\n"), *argv );

            return ERROR_RETURN_CODE;
        }
    }

    // fork the process
    //
    // NB: do *not* use vfork() here, it completely breaks this code for some
    //     reason under Solaris (and maybe others, although not under Linux)
    //     But on OpenVMS we do not have fork so we have to use vfork and
    //     cross our fingers that it works.
#ifdef __VMS
   pid_t pid = vfork();
#else
   pid_t pid = fork();
#endif
   if ( pid == -1 )     // error?
    {
        wxLogSysError( _("Fork failed") );

        return ERROR_RETURN_CODE;
    }
    else if ( pid == 0 )  // we're in child
    {
        // NB: we used to close all the unused descriptors of the child here
        //     but this broke some programs which relied on e.g. FD 1 being
        //     always opened so don't do it any more, after all there doesn't
        //     seem to be any real problem with keeping them opened

#if !defined(__VMS) && !defined(__EMX__)
        if ( flags & wxEXEC_MAKE_GROUP_LEADER )
        {
            // Set process group to child process' pid.  Then killing -pid
            // of the parent will kill the process and all of its children.
            setsid();
        }
#endif // !__VMS

        // redirect stdin, stdout and stderr
        if ( pipeIn.IsOk() )
        {
            if ( dup2(pipeIn[wxPipe::Read], STDIN_FILENO) == -1 ||
                 dup2(pipeOut[wxPipe::Write], STDOUT_FILENO) == -1 ||
                 dup2(pipeErr[wxPipe::Write], STDERR_FILENO) == -1 )
            {
                wxLogSysError(_("Failed to redirect child process input/output"));
            }

            pipeIn.Close();
            pipeOut.Close();
            pipeErr.Close();
        }

        // Close all (presumably accidentally) inherited file descriptors to
        // avoid descriptor leaks. This means that we don't allow inheriting
        // them purposefully but this seems like a lesser evil in wx code.
        // Ideally we'd provide some flag to indicate that none (or some?) of
        // the descriptors do not need to be closed but for now this is better
        // than never closing them at all as wx code never used FD_CLOEXEC.

        // Note that while the reading side of the end process detection pipe
        // can be safely closed, we should keep the write one opened, it will
        // be only closed when the process terminates resulting in a read
        // notification to the parent
        const int fdEndProc = execData.pipeEndProcDetect.Detach(wxPipe::Write);
        execData.pipeEndProcDetect.Close();

        // TODO: Iterating up to FD_SETSIZE is both inefficient (because it may
        //       be quite big) and incorrect (because in principle we could
        //       have more opened descriptions than this number). Unfortunately
        //       there is no good portable solution for closing all descriptors
        //       above a certain threshold but non-portable solutions exist for
        //       most platforms, see [http://stackoverflow.com/questions/899038/
        //          getting-the-highest-allocated-file-descriptor]
        for ( int fd = 0; fd < (int)FD_SETSIZE; ++fd )
        {
            if ( fd != STDIN_FILENO  &&
                 fd != STDOUT_FILENO &&
                 fd != STDERR_FILENO &&
                 fd != fdEndProc )
            {
                close(fd);
            }
        }


        // Process additional options if we have any
        if ( env )
        {
            // Change working directory if it is specified
            if ( !env->cwd.empty() )
                wxSetWorkingDirectory(env->cwd);

            // Change environment if needed.
            //
            // NB: We can't use execve() currently because we allow using
            //     non full paths to wxExecute(), i.e. we want to search for
            //     the program in PATH. However it just might be simpler/better
            //     to do the search manually and use execve() envp parameter to
            //     set up the environment of the child process explicitly
            //     instead of doing what we do below.
            if ( !env->env.empty() )
            {
                wxEnvVariableHashMap oldenv;
                wxGetEnvMap(&oldenv);

                // Remove unwanted variables
                wxEnvVariableHashMap::const_iterator it;
                for ( it = oldenv.begin(); it != oldenv.end(); ++it )
                {
                    if ( env->env.find(it->first) == env->env.end() )
                        wxUnsetEnv(it->first);
                }

                // And add the new ones (possibly replacing the old values)
                for ( it = env->env.begin(); it != env->env.end(); ++it )
                    wxSetEnv(it->first, it->second);
            }
        }

        execvp(*argv, argv);

        fprintf(stderr, "execvp(");
        for ( char **a = argv; *a; a++ )
            fprintf(stderr, "%s%s", a == argv ? "" : ", ", *a);
        fprintf(stderr, ") failed with error %d!\n", errno);

        // there is no return after successful exec()
        _exit(-1);

        // some compilers complain about missing return - of course, they
        // should know that exit() doesn't return but what else can we do if
        // they don't?
        //
        // and, sure enough, other compilers complain about unreachable code
        // after exit() call, so we can just always have return here...
#if defined(__VMS) || defined(__INTEL_COMPILER)
        return 0;
#endif
    }
    else // we're in parent
    {
        // save it for WaitForChild() use
        execData.pid = pid;
        if (execData.process)
            execData.process->SetPid(pid);  // and also in the wxProcess

        // prepare for IO redirection

#if HAS_PIPE_STREAMS
        // the input buffer bufOut is connected to stdout, this is why it is
        // called bufOut and not bufIn
        wxStreamTempInputBuffer bufOut,
                                bufErr;

        if ( process && process->IsRedirected() )
        {
            // Avoid deadlocks which could result from trying to write to the
            // child input pipe end while the child itself is writing to its
            // output end and waiting for us to read from it.
            if ( !pipeIn.MakeNonBlocking(wxPipe::Write) )
            {
                // This message is not terrible useful for the user but what
                // else can we do? Also, should we fail here or take the risk
                // to continue and deadlock? Currently we choose the latter but
                // it might not be the best idea.
                wxLogSysError(_("Failed to set up non-blocking pipe, "
                                "the program might hang."));
#if wxUSE_LOG
                wxLog::FlushActive();
#endif
            }

            wxOutputStream *inStream =
                new wxPipeOutputStream(pipeIn.Detach(wxPipe::Write));

            const int fdOut = pipeOut.Detach(wxPipe::Read);
            wxPipeInputStream *outStream = new wxPipeInputStream(fdOut);

            const int fdErr = pipeErr.Detach(wxPipe::Read);
            wxPipeInputStream *errStream = new wxPipeInputStream(fdErr);

            process->SetPipeStreams(outStream, inStream, errStream);

            bufOut.Init(outStream);
            bufErr.Init(errStream);

            execData.bufOut = &bufOut;
            execData.bufErr = &bufErr;

            execData.fdOut = fdOut;
            execData.fdErr = fdErr;
        }
#endif // HAS_PIPE_STREAMS

        if ( pipeIn.IsOk() )
        {
            pipeIn.Close();
            pipeOut.Close();
            pipeErr.Close();
        }

        // we want this function to work even if there is no wxApp so ensure
        // that we have a valid traits pointer
        wxConsoleAppTraits traitsConsole;
        wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
        if ( !traits )
            traits = &traitsConsole;

        return traits->WaitForChild(execData);
    }

#if !defined(__VMS) && !defined(__INTEL_COMPILER)
    return ERROR_RETURN_CODE;
#endif
}