void NETBuildSystem::HandleToolUpdate(StringHash eventType, VariantMap& eventData) { if (curBuild_.Null() && !builds_.Size()) return; if (curBuild_.Null()) { // kick off a new build curBuild_ = builds_.Front(); builds_.PopFront(); FileSystem* fileSystem = GetSubsystem<FileSystem>(); // Ensure solution still exists if (!fileSystem->FileExists(curBuild_->solutionPath_)) { CurrentBuildError(ToString("Solution does not exist(%s)", curBuild_->solutionPath_.CString())); return; } String solutionPath = curBuild_->solutionPath_; String ext = GetExtension(solutionPath); bool requiresNuGet = true; if (ext == ".sln") { // TODO: handle projects that require nuget requiresNuGet = false; if (!fileSystem->FileExists(solutionPath)) { CurrentBuildError(ToString("Generated solution does not exist (%s : %s)", curBuild_->solutionPath_.CString(), solutionPath.CString())); return; } } else if (ext == ".json") { SharedPtr<NETProjectGen> gen(new NETProjectGen(context_)); gen->SetSupportedPlatforms(curBuild_->platforms_); gen->SetRewriteSolution(true); if (!gen->LoadJSONProject(solutionPath)) { CurrentBuildError(ToString("Error loading project (%s)", solutionPath.CString())); return; } if (!gen->Generate()) { CurrentBuildError(ToString("Error generating project (%s)", solutionPath.CString())); return; } solutionPath = gen->GetSolution()->GetOutputFilename(); requiresNuGet = gen->GetRequiresNuGet(); if (!fileSystem->FileExists(solutionPath)) { CurrentBuildError(ToString("Generated solution does not exist (%s : %s)", curBuild_->solutionPath_.CString(), solutionPath.CString())); return; } } ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>(); const String& nugetBinary = tenv->GetAtomicNETNuGetBinary(); if (requiresNuGet && !fileSystem->FileExists(nugetBinary)) { CurrentBuildError(ToString("NuGet binary is missing (%s)", nugetBinary.CString())); return; } StringVector stringVector; String platforms; StringVector processedPlatforms; String configs; for (unsigned i = 0; i < curBuild_->configurations_.Size(); i++) { stringVector.Push(ToString("/p:Configuration=%s", curBuild_->configurations_[i].CString())); } configs = String::Joined(stringVector, " "); stringVector.Clear(); for (unsigned i = 0; i < curBuild_->platforms_.Size(); i++) { // map platform String platform = curBuild_->platforms_[i]; if (platform == "windows" || platform == "macosx" || platform == "linux") { ATOMIC_LOGINFOF("Platform \"%s\" mapped to \"desktop\"", platform.CString()); platform = "desktop"; } if (processedPlatforms.Contains(platform)) { ATOMIC_LOGWARNINGF("Platform \"%s\" is duplicated, skipping", platform.CString()); continue; } processedPlatforms.Push(platform); if (platform == "desktop" || platform == "android") { platform = "\"Any CPU\""; } else if (platform == "ios") { platform = "\"Any CPU\""; // TODO // platform = "iPhone"; } else { ATOMIC_LOGERRORF("Unknown platform: %s, skipping", platform.CString()); continue; } platform = ToString("/p:Platform=%s", platform.CString()); if (stringVector.Contains(platform)) { // This can happen when specifying Desktop + Android for example continue; } stringVector.Push(platform); } platforms = String::Joined(stringVector, " "); stringVector.Clear(); Vector<String> args; #ifdef ATOMIC_PLATFORM_WINDOWS String cmdToolsPath = Poco::Environment::get("VS140COMNTOOLS", "").c_str(); if (!cmdToolsPath.Length()) { CurrentBuildError("VS140COMNTOOLS environment variable not found, cannot proceed"); return; } if (!cmdToolsPath.EndsWith("\\")) { cmdToolsPath += "\\"; } String msbuildcmd = ToString("%sVsMSBuildCmd.bat", cmdToolsPath.CString()); String cmd = "cmd"; args.Push("/A"); args.Push("/C"); // vcvars bat String compile = ToString("\"\"%s\" ", msbuildcmd.CString()); if (requiresNuGet) { compile += ToString("&& \"%s\" restore \"%s\" ", nugetBinary.CString(), solutionPath.CString()); } compile += ToString("&& msbuild \"%s\" %s %s", solutionPath.CString(), platforms.CString(), configs.CString()); if (curBuild_->targets_.Size()) { StringVector targets; for (unsigned i = 0; i < curBuild_->targets_.Size(); i++) { const char* tname = curBuild_->targets_[i].CString(); targets.Push(ToString("/t:\"%s:Rebuild\"", tname)); } compile += " " + String::Joined(targets, " "); } // close out quote compile += "\""; args.Push(compile); #else String compile; String cmd = "bash"; args.Push("-c"); String xbuildBinary = tenv->GetMonoExecutableDir() + "xbuild"; if (requiresNuGet) { #ifdef ATOMIC_PLATFORM_OSX compile += ToString("\"%s\" restore \"%s\" && ", nugetBinary.CString(), solutionPath.CString()); #else compile += ToString("mono \"%s\" restore \"%s\" && ", nugetBinary.CString(), solutionPath.CString()); #endif } compile += ToString("\"%s\" \"%s\" %s %s", xbuildBinary.CString(), solutionPath.CString(), platforms.CString(), configs.CString()); if (curBuild_->targets_.Size()) { StringVector targets; for (unsigned i = 0; i < curBuild_->targets_.Size(); i++) { const char* tname = curBuild_->targets_[i].CString(); targets.Push(ToString("%s:Rebuild", tname)); } compile += " /target:\"" + String::Joined(targets, ";") + "\""; } args.Push(compile); #endif curBuild_->allArgs_.Join(args, " "); SubprocessSystem* subs = GetSubsystem<SubprocessSystem>(); Subprocess* subprocess = nullptr; ATOMIC_LOGINFOF("%s : %s", cmd.CString(), curBuild_->allArgs_.CString()); try { subprocess = subs->Launch(cmd, args, ""); } catch (Poco::SystemException) { subprocess = nullptr; } if (!subprocess) { CurrentBuildError(ToString("NETCompile::Compile - Unable to launch MSBuild subprocess\n%s", curBuild_->allArgs_.CString())); return; } VariantMap buildBeginEventData; buildBeginEventData[NETBuildBegin::P_BUILD] = curBuild_; SendEvent(E_NETBUILDBEGIN, buildBeginEventData); SubscribeToEvent(subprocess, E_SUBPROCESSCOMPLETE, ATOMIC_HANDLER(NETBuildSystem, HandleCompileProcessComplete)); SubscribeToEvent(subprocess, E_SUBPROCESSOUTPUT, ATOMIC_HANDLER(NETBuildSystem, HandleSubprocessOutput)); curBuild_->status_ = NETBUILD_BUILDING; } }