diff --git a/indra/llinventory/llpermissions.cpp b/indra/llinventory/llpermissions.cpp index 4b6d0f74d4..2608f5e480 100755 --- a/indra/llinventory/llpermissions.cpp +++ b/indra/llinventory/llpermissions.cpp @@ -1149,8 +1149,18 @@ void mask_to_string(U32 mask, char* str) else { *str = ' '; - } - str++; + } +// OpenSim export permission + if (mask & PERM_EXPORT) + { + *str = 'X'; + } + else + { + *str = ' '; + } +// + str++; *str = '\0'; } diff --git a/indra/llinventory/llpermissions.h b/indra/llinventory/llpermissions.h index 3ecc922370..0307377581 100755 --- a/indra/llinventory/llpermissions.h +++ b/indra/llinventory/llpermissions.h @@ -274,6 +274,7 @@ public: inline bool allowModifyBy(const LLUUID &agent_id, const LLUUID& group) const; inline bool allowCopyBy(const LLUUID& agent_id, const LLUUID& group) const; inline bool allowMoveBy(const LLUUID &agent_id, const LLUUID &group) const; + inline bool allowExportBy(const LLUUID& agent_id) const; // OpenSim export permission // This somewhat specialized function is meant for testing if the // current owner is allowed to transfer to the specified agent id. @@ -373,6 +374,13 @@ bool LLPermissions::allowTransferTo(const LLUUID &agent_id) const } } +// Opensim Export Permissions +bool LLPermissions::allowExportBy(const LLUUID& agent) const +{ + return ((mCreator == agent) ? true : (allowOperationBy(PERM_EXPORT, agent, LLUUID::null))); +} +// + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLAggregatePermissions // diff --git a/indra/llinventory/llpermissionsflags.h b/indra/llinventory/llpermissionsflags.h index 9ecf465c8c..4d81989207 100755 --- a/indra/llinventory/llpermissionsflags.h +++ b/indra/llinventory/llpermissionsflags.h @@ -46,6 +46,10 @@ const PermissionBit PERM_MODIFY = (1 << 14); // 0x00004000 // objects, allow copy const PermissionBit PERM_COPY = (1 << 15); // 0x00008000 +// OpenSim export permission +const PermissionBit PERM_EXPORT = (1 << 16); // 0x00010000 +// + // parcels, allow entry, deprecated //const PermissionBit PERM_ENTER = (1 << 16); // 0x00010000 diff --git a/indra/newview/NACLantispam.cpp b/indra/newview/NACLantispam.cpp index 4e324228c9..f445a8b536 100644 --- a/indra/newview/NACLantispam.cpp +++ b/indra/newview/NACLantispam.cpp @@ -156,7 +156,7 @@ S32 NACLAntiSpamQueue::checkEntry(const LLUUID& name, U32 multiplier) } else { - lldebugs << "[antispam] New queue entry:" << name.asString() << llendl; + LL_DEBUGS("AntiSpam") << "New queue entry:" << name.asString() << LL_ENDL; NACLAntiSpamQueueEntry* entry = new NACLAntiSpamQueueEntry(); entry->updateEntryAmount(); entry->updateEntryTime(); @@ -371,14 +371,16 @@ bool NACLAntiSpamRegistry::checkQueue(EAntispamQueue queue, const LLUUID& source if (result == 1) // newly blocked, result == 1 { - std::string msg = llformat("AntiSpam: Blocked %s for spamming a %s (%d) times in %d seconds.", source.asString().c_str(), getQueueName(queue), multiplier * mQueues[queue]->getAmount(), mQueues[queue]->getTime()); if (!silent) { LLSD args; - args["MESSAGE"] = msg; - LLNotificationsUtil::add("SystemMessageTip", args); + args["SOURCE"] = source.asString(); + args["QUEUE"] = getQueueName(queue); + args["COUNT"] = llformat("%d", multiplier * mQueues[queue]->getAmount()); + args["PERIOD"] = llformat("%d", mQueues[queue]->getTime()); + LLNotificationsUtil::add("AntiSpamBlocked", args); } - llinfos << "[antispam] " << msg << llendl; + LL_INFOS("AntiSpam") << "Blocked " << source.asString() << " for spamming a " << getQueueName(queue) << " (" << multiplier * mQueues[queue]->getAmount() << ") times in " << mQueues[queue]->getTime() << " seconds." << LL_ENDL; return true; } @@ -437,7 +439,7 @@ void NACLAntiSpamRegistry::purgeAllQueues() } } } - llinfos << "AntiSpam Queues Purged" << llendl; + LL_INFOS("AntiSpam") << "AntiSpam Queues Purged" << LL_ENDL; } S32 NACLAntiSpamRegistry::checkGlobalEntry(const LLUUID& source, U32 multiplier) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index cea188e9aa..43f38b5b85 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -20899,7 +20899,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type Boolean Value - 1 + 0 FSLegacyRadarFriendColoring diff --git a/indra/newview/fsexport.cpp b/indra/newview/fsexport.cpp index 6cd2c9ae4c..3343c8c158 100644 --- a/indra/newview/fsexport.cpp +++ b/indra/newview/fsexport.cpp @@ -295,13 +295,28 @@ void FSExport::addPrim(LLViewerObject* object, bool root) default_prim = false; } #if OPENSIM - if (LLGridManager::getInstance()->isInOpenSim() - && object->permYouOwner() - && object->permModify() - && object->permCopy() - && object->permTransfer()) + if (LLGridManager::getInstance()->isInOpenSim()) { - default_prim = false; + LLViewerRegion* region = gAgent.getRegion(); + if (region->regionSupportsExport() == LLViewerRegion::EXPORT_ALLOWED) + { + default_prim = !node->mPermissions->allowExportBy(gAgent.getID()); + } + else if (region->regionSupportsExport() == LLViewerRegion::EXPORT_DENIED) + { + // Only your own creations if this is explicitly set + default_prim = (!(object->permYouOwner() + && gAgentID == node->mPermissions->getCreator())); + } + /// TODO: Once enough grids adopt a version supporting the exports cap, get consensus + /// on whether we should allow full perm exports anymore. + else // LLViewerRegion::EXPORT_UNDEFINED + { + default_prim = (!(object->permYouOwner() + && object->permModify() + && object->permCopy() + && object->permTransfer())); + } } #endif } @@ -748,9 +763,23 @@ bool FSExport::assetCheck(LLUUID asset_id, std::string& name, std::string& descr { LLPermissions perms = items[i]->getPermissions(); #if OPENSIM - if (LLGridManager::getInstance()->isInOpenSim() && ((perms.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)) + if (LLGridManager::getInstance()->isInOpenSim()) { - exportable = true; + LLViewerRegion* region = gAgent.getRegion(); + if (region->regionSupportsExport() == LLViewerRegion::EXPORT_ALLOWED) + { + exportable = (perms.getMaskOwner() & PERM_EXPORT) == PERM_EXPORT; + } + else if (region->regionSupportsExport() == LLViewerRegion::EXPORT_DENIED) + { + exportable = perms.getCreator() == gAgentID; + } + /// TODO: Once enough grids adopt a version supporting the exports cap, get consensus + /// on whether we should allow full perm exports anymore. + else + { + exportable = (perms.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED; + } } #endif if (LLGridManager::getInstance()->isInSecondLife() && (perms.getCreator() == gAgentID)) @@ -785,9 +814,23 @@ void FSExport::inventoryChanged(LLViewerObject* object, LLInventoryObject::objec bool exportable = false; LLPermissions perms = item->getPermissions(); #if OPENSIM - if (LLGridManager::getInstance()->isInOpenSim() && ((perms.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)) + if (LLGridManager::getInstance()->isInOpenSim()) { - exportable = true; + LLViewerRegion* region = gAgent.getRegion(); + if (region->regionSupportsExport() == LLViewerRegion::EXPORT_ALLOWED) + { + exportable = (perms.getMaskOwner() & PERM_EXPORT) == PERM_EXPORT; + } + else if (region->regionSupportsExport() == LLViewerRegion::EXPORT_DENIED) + { + exportable = perms.getCreator() == gAgentID; + } + /// TODO: Once enough grids adopt a version supporting the exports cap, get consensus + /// on whether we should allow full perm exports anymore. + else + { + exportable = ((perms.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED); + } } #endif if (LLGridManager::getInstance()->isInSecondLife() && (perms.getCreator() == gAgentID)) diff --git a/indra/newview/fsfloatersearch.cpp b/indra/newview/fsfloatersearch.cpp index 8c64f99e7c..b1b709837b 100644 --- a/indra/newview/fsfloatersearch.cpp +++ b/indra/newview/fsfloatersearch.cpp @@ -2921,10 +2921,14 @@ void FSPanelSearchWeb::loadURL(const FSFloaterSearch::SearchQuery &p) url = debug_url; } else if(LLGridManager::getInstance()->isInOpenSim()) - { - url = LLLoginInstance::getInstance()->hasResponse("search") - ? LLLoginInstance::getInstance()->getResponse("search").asString() - : gSavedSettings.getString("SearchURLOpenSim"); + { + std::string os_search_url = gAgent.getRegion()->getSearchServerURL(); + if (!os_search_url.empty()) + url = os_search_url; + else if (LLLoginInstance::getInstance()->hasResponse("search")) + url = LLLoginInstance::getInstance()->getResponse("search").asString(); + else + url = gSavedSettings.getString("SearchURLOpenSim"); } else #endif // OPENSIM diff --git a/indra/newview/fsradar.cpp b/indra/newview/fsradar.cpp index eb9f191b0d..dba46ed132 100644 --- a/indra/newview/fsradar.cpp +++ b/indra/newview/fsradar.cpp @@ -170,7 +170,6 @@ void FSRadar::updateRadarList() static LLCachedControl sUseLSLBridge(gSavedSettings, "UseLSLBridge"); static LLCachedControl RenderFarClip(gSavedSettings, "RenderFarClip"); static LLCachedControl sFSLegacyRadarFriendColoring(gSavedSettings, "FSLegacyRadarFriendColoring"); - static LLCachedControl sFSLegacyRadarLindenColoring(gSavedSettings, "FSLegacyRadarLindenColoring"); F32 drawRadius(RenderFarClip); const LLVector3d& posSelf = gAgent.getPositionGlobal(); @@ -514,22 +513,12 @@ void FSRadar::updateRadarList() } entry_options["name_style"] = nameCellStyle; - // TODO: Decide whether we want special colored names in the radar or let the current UI suffice - //LLColor4 name_color = LGGContactSets::getInstance()->colorize(avId, range_color, LGG_CS_RADAR); - //entry_options["name_color"] = name_color.getValue(); - LLColor4 name_color; + LLColor4 name_color = LGGContactSets::getInstance()->colorize(avId, range_color, LGG_CS_RADAR); + if (LGGContactSets::getInstance()->hasFriendColorThatShouldShow(avId, LGG_CS_RADAR)) { name_color = LGGContactSets::getInstance()->getFriendColor(avId); } - else if (FSCommon::isLinden(avId) && sFSLegacyRadarLindenColoring) - { - name_color = LLUIColorTable::instance().getColor("MapAvatarLindenColor", LLColor4::black); - } - else if (relation && sFSLegacyRadarFriendColoring) - { - name_color = LLUIColorTable::instance().getColor("MapAvatarFriendColor", LLColor4::black); - } entry_options["name_color"] = name_color.getValue(); // Voice power level indicator diff --git a/indra/newview/lggcontactsets.cpp b/indra/newview/lggcontactsets.cpp index 6ad0e9d361..3c4f08e9e5 100644 --- a/indra/newview/lggcontactsets.cpp +++ b/indra/newview/lggcontactsets.cpp @@ -340,6 +340,8 @@ LLColor4 LGGContactSets::getGroupColor(const std::string& groupName) LLColor4 LGGContactSets::colorize(const LLUUID& uuid, const LLColor4& cur_color, ELGGCSType type) { + static LLCachedControl legacy_radar_friend(gSavedSettings, "FSLegacyRadarFriendColoring"); + static LLCachedControl legacy_radar_linden(gSavedSettings, "FSLegacyRadarLindenColoring"); LLColor4 color = cur_color; // Leave generic colors if RLV restricted @@ -405,6 +407,9 @@ LLColor4 LGGContactSets::colorize(const LLUUID& uuid, const LLColor4& cur_color, color = LLUIColorTable::instance().getColor("MapAvatarFriendColor", LLColor4::white); break; case LGG_CS_RADAR: + if (legacy_radar_friend) + color = LLUIColorTable::instance().getColor("MapAvatarFriendColor", LLColor4::white); + break; default: llwarns << "Unhandled colorize case!" << llendl; break; @@ -446,6 +451,9 @@ LLColor4 LGGContactSets::colorize(const LLUUID& uuid, const LLColor4& cur_color, color = LLUIColorTable::instance().getColor("MapAvatarLindenColor", LLColor4::blue); break; case LGG_CS_RADAR: + if (legacy_radar_linden) + color = LLUIColorTable::instance().getColor("MapAvatarLindenColor", LLColor4::blue); + break; default: llwarns << "Unhandled colorize case!" << llendl; break; diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 09241139f5..466de8feed 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -2327,6 +2327,12 @@ void LLAgent::endAnimationUpdateUI() mViewsPushed = FALSE; gFocusMgr.setKeyboardFocus(NULL); // make sure no floater has focus after restoring them } + // FIRE-11312: Exiting Mouselook puts keyboard focus on Nearby Chat bar + else if(gSavedSettings.getBOOL("FSShowInterfaceInMouselook")) + { + gFocusMgr.setKeyboardFocus(NULL); + } + // gAgentCamera.setLookAt(LOOKAT_TARGET_CLEAR); diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp index bd6de71683..a869a53afd 100755 --- a/indra/newview/llfloatersearch.cpp +++ b/indra/newview/llfloatersearch.cpp @@ -38,8 +38,9 @@ #include "llui.h" #include "llviewercontrol.h" #include "llweb.h" -#include "llviewernetwork.h"// +#include "llviewernetwork.h"// +#include "llviewerregion.h" // Opensim search support // support secondlife:///app/search/{CATEGORY}/{QUERY} SLapps class LLSearchHandler : public LLCommandHandler @@ -223,9 +224,13 @@ void LLFloaterSearch::search(const SearchQuery &p) } else if(LLGridManager::getInstance()->isInOpenSim()) { - url = LLLoginInstance::getInstance()->hasResponse("search") - ? LLLoginInstance::getInstance()->getResponse("search").asString() - : gSavedSettings.getString("SearchURLOpenSim"); + std::string os_search_url = gAgent.getRegion()->getSearchServerURL(); + if (!os_search_url.empty()) + url = os_search_url; + else if (LLLoginInstance::getInstance()->hasResponse("search")) + url = LLLoginInstance::getInstance()->getResponse("search").asString(); + else + url = gSavedSettings.getString("SearchURLOpenSim"); } else // we are in SL or SL beta #endif // OPENSIM // diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 1a0b6fcffe..fd7098b931 100755 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -3903,14 +3903,14 @@ bool process_login_success_response(U32 &first_sim_size_x, U32 &first_sim_size_y { // We got an answer from the grid -> use that for map for the current session gSavedSettings.setString("WebProfileURL", web_profile_url); - LL_INFOS("LLStartup") << "map-server-url : we got an answer from the grid : " << web_profile_url << LL_ENDL; + LL_INFOS("LLStartup") << "web-profile-url : we got an answer from the grid : " << web_profile_url << LL_ENDL; } else { // No answer from the grid -> use the default setting for current session web_profile_url = "https://my.secondlife.com/[AGENT_NAME]"; gSavedSettings.setString("WebProfileURL", web_profile_url); - LL_INFOS("LLStartup") << "web_profile_url : no web_profile_url answer, we use the default setting for the web : " << web_profile_url << LL_ENDL; + LL_INFOS("LLStartup") << "web-profile-url : no web_profile_url answer, we use the default setting for the web : " << web_profile_url << LL_ENDL; } // FIRE-10567 - Set classified fee, if it's available. if (response.has("classified_fee")) diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp old mode 100755 new mode 100644 index 2233856bdf..3137a3bc33 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -210,22 +210,20 @@ bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, const std::string // LLURLDispatcherImpl::regionNameCallback, // slurl.getSLURLString(), LLSLURL hyper = slurl; - std::string region = hyper.getRegion(); - std::string dest = hyper.getSLURLString(); #ifdef OPENSIM // std::string grid = slurl.getGrid(); std::string current_grid = LLGridManager::getInstance()->getGrid(); - std::string gatekeeper = LLGridManager::getInstance()->getGatekeeper(grid); - std::string current = LLGridManager::getInstance()->getGrid(); - if((grid != current ) && (!LLGridManager::getInstance()->isInOpenSim() || (!slurl.getHypergrid() && gatekeeper.empty()))) + if ((grid != current_grid) + && (!LLGridManager::getInstance()->isInOpenSim() || (!slurl.getHypergrid() && gatekeeper.empty()))) { + std::string dest = hyper.getSLURLString(); if (!dest.empty()) { LLSD args; args["SLURL"] = dest; - args["GRID"] = slurl.getGrid(); + args["GRID"] = grid; args["CURRENT_GRID"] = current_grid; LLNotificationsUtil::add("CantTeleportToGrid", args); return true; @@ -235,28 +233,11 @@ bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, const std::string { hyper = LLSLURL(gatekeeper + ":" + slurl.getRegion(), slurl.getPosition(), true); } - - // Trim the grid uri if we're talking a local region here. - if (grid == current) - { - std::size_t pos; - pos = region.find(current); - if (pos != std::string::npos) - { - region.erase(pos, pos + current.length() + 1); - } - - pos = dest.find(current); - if (pos != std::string::npos) - { - dest.erase(pos, pos + current.length() + 1); - } - } #endif //OPENSIM - // Request a region handle by name - LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region, LLURLDispatcherImpl::regionNameCallback, dest, LLUI::sSettingGroups["config"]->getBOOL("SLURLTeleportDirectly")); // don't teleport // + // Request a region handle by name + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(hyper.getRegion(), LLURLDispatcherImpl::regionNameCallback, hyper.getSLURLString(), LLUI::sSettingGroups["config"]->getBOOL("SLURLTeleportDirectly")); // don't teleport return true; } diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 9986b280de..eb2ba63e42 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -2062,7 +2062,28 @@ bool LLViewerRegion::dynamicPathfindingEnabled() const } // OpenSim Extras support -#ifdef OPENSIM +LLViewerRegion::EOSExportSupport LLViewerRegion::regionSupportsExport() const +{ +#ifndef OPENSIM + return EXPORT_UNDEFINED; // Second Life regions never support export. +} +#else // OPENSIM + EOSExportSupport export_support = EXPORT_UNDEFINED; + if ((mSimulatorFeatures.has("OpenSimExtras")) + && (mSimulatorFeatures["OpenSimExtras"].has("ExportSupported"))) + { + if (mSimulatorFeatures["OpenSimExtras"]["ExportSupported"].asBoolean()) + { + export_support = EXPORT_ALLOWED; + } + else + { + export_support = EXPORT_DENIED; + } + } + return export_support; +} + // HG Maps std::string LLViewerRegion::getHGMapServerURL() const { @@ -2073,5 +2094,16 @@ std::string LLViewerRegion::getHGMapServerURL() const } return url; } -#endif + +// OS Search URL +std::string LLViewerRegion::getSearchServerURL() const +{ + std::string url = ""; + if (mSimulatorFeatures.has("OpenSimExtras") && mSimulatorFeatures["OpenSimExtras"].has("search-server-url")) + { + url = mSimulatorFeatures["OpenSimExtras"]["search-server-url"].asString(); + } + return url; +} +#endif // OPENSIM // diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index a322a36c88..da97f2b3d2 100755 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -319,8 +319,17 @@ public: bool dynamicPathfindingEnabled() const; // Opensim Extras support + typedef enum e_os_export_support + { + EXPORT_UNDEFINED = 0, + EXPORT_ALLOWED, + EXPORT_DENIED + } EOSExportSupport; + + EOSExportSupport regionSupportsExport() const; // ExportSupports #ifdef OPENSIM std::string getHGMapServerURL() const; // HG Maps + std::string getSearchServerURL() const; // OS Search URL #endif // OPENSIM // diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 9c9fce5080..07c536cf4c 100755 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -11429,5 +11429,13 @@ You are about to eject [COUNT] members from the group. Export Export finished and saved to [FILENAME]. + + +AntiSpam: Blocked ["SOURCE"] for spamming a ["QUEUE"] (["COUNT"]) times in [PERIOD] seconds. + diff --git a/indra/newview/skins/default/xui/pl/notifications.xml b/indra/newview/skins/default/xui/pl/notifications.xml index bf134fe427..21620ebc65 100755 --- a/indra/newview/skins/default/xui/pl/notifications.xml +++ b/indra/newview/skins/default/xui/pl/notifications.xml @@ -4473,4 +4473,7 @@ Nie można tego cofnąć. Wyeksportowano i zapisano do [FILENAME]. + + Antyspam: Zablokowano ["SOURCE"] ze względu na spam ["QUEUE"] (["COUNT"]) razy podczas [PERIOD] sekund. + diff --git a/scripts/configure_firestorm.sh b/scripts/configure_firestorm.sh index 6e99f8ad36..7f9d02b020 100755 --- a/scripts/configure_firestorm.sh +++ b/scripts/configure_firestorm.sh @@ -60,6 +60,7 @@ showUsage() echo " --btype [Release|RelWithDebInfo] : Release is default, whether to use symbols" echo " --kdu : Build with KDU" echo " --package : Build installer" + echo " --no-package : Build without installer (Overrides --package)" echo " --fmodex : Build with FMOD Ex" echo " --opensim : Build with OpenSim support (Disables Havok features)" echo " --no-opensim : Build without OpenSim support (Overrides --opensim)" @@ -75,7 +76,7 @@ getArgs() # $* = the options passed in from main { if [ $# -gt 0 ]; then - while getoptex "clean build config version package fmodex jobs: platform: kdu opensim no-opensim avx help chan: btype:" "$@" ; do + while getoptex "clean build config version package no-package fmodex jobs: platform: kdu opensim no-opensim avx help chan: btype:" "$@" ; do #insure options are valid if [ -z "$OPTOPT" ] ; then @@ -98,6 +99,7 @@ getArgs() no-opensim) WANTS_OPENSIM=$FALSE;; avx) WANTS_AVX=$TRUE;; package) WANTS_PACKAGE=$TRUE;; + no-package) WANTS_PACKAGE=$FALSE;; build) WANTS_BUILD=$TRUE;; platform) PLATFORM="$OPTARG";; jobs) JOBS="$OPTARG";;