mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Merge branch 'master' into beacons
This commit is contained in:
commit
42ec0d6332
@ -3,7 +3,6 @@
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/lua/5.1/bin/luadocumentor.bat}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value=""Moose\Core\*.lua" "Moose\Wrapper\*.lua" "Moose\Actions\*.lua" "Moose\Functional\*.lua" "Moose\Tasking\*.lua" "Moose\Utilities\*.lua" "Moose\AI\*.lua" --dir "${workspace_loc:/Moose_Framework/docs/Documentation} --style ${workspace_loc:/Moose_Framework/docs/Stylesheet/stylesheet.css}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Development}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/luadocumentor.bat}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Utils}"/>
|
||||
</launchConfiguration>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/lua/5.1/bin/lua.exe}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/luarocks/lua5.1.exe}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value=""Moose_Create.lua" "D" "${current_date}" "${workspace_loc:/Moose_Framework//Moose Development/Moose}" "${workspace_loc:/Moose_Framework/Moose Mission Setup}""/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Mission Setup}"/>
|
||||
</launchConfiguration>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/lua/5.1/bin/lua.exe}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/luarocks/lua5.1.exe}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value=""Moose_Create.lua" "S" "${current_date}" "${workspace_loc:/Moose_Framework//Moose Development/Moose}" "${workspace_loc:/Moose_Framework/Moose Mission Setup}""/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Mission Setup}"/>
|
||||
</launchConfiguration>
|
||||
|
||||
@ -4,6 +4,5 @@
|
||||
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Moose Mission Setup/Moose Mission Update/Moose_Update_Missions.bat}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value=""${folder_prompt}""/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Mission Setup/Moose Mission Update}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Missions}"/>
|
||||
</launchConfiguration>
|
||||
|
||||
@ -4,62 +4,22 @@
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
-- ====
|
||||
--
|
||||
-- # 1) @{AI_Balancer#AI_BALANCER} class, extends @{Fsm#FSM_SET}
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- The @{AI_Balancer#AI_BALANCER} class monitors and manages as many replacement AI groups as there are
|
||||
-- CLIENTS in a SET_CLIENT collection, which are not occupied by human players.
|
||||
-- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions.
|
||||
-- ### [AI_BALANCER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/AIB%20-%20AI%20Balancing)
|
||||
--
|
||||
-- The parent class @{Fsm#FSM_SET} manages the functionality to control the Finite State Machine (FSM).
|
||||
-- The mission designer can tailor the behaviour of the AI_BALANCER, by defining event and state transition methods.
|
||||
-- An explanation about state and event transition methods can be found in the @{FSM} module documentation.
|
||||
-- ### [AI_BALANCER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AIB%20-%20AI%20Balancing)
|
||||
--
|
||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||
--
|
||||
-- The mission designer can tailor the AI_BALANCER behaviour, by implementing a state or event handling method for the following:
|
||||
-- ====
|
||||
--
|
||||
-- * **@{#AI_BALANCER.OnAfterSpawned}**( AISet, From, Event, To, AIGroup ): Define to add extra logic when an AI is spawned.
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ## 1.1) AI_BALANCER construction
|
||||
-- ### [AI_BALANCER YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl2CJVIrL1TdAumuVS8n64B7)
|
||||
--
|
||||
-- Create a new AI_BALANCER object with the @{#AI_BALANCER.New}() method:
|
||||
--
|
||||
-- ## 1.2) AI_BALANCER is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 1.2.1) AI_BALANCER States
|
||||
--
|
||||
-- * **Monitoring** ( Set ): Monitoring the Set if all AI is spawned for the Clients.
|
||||
-- * **Spawning** ( Set, ClientName ): There is a new AI group spawned with ClientName as the name of reference.
|
||||
-- * **Spawned** ( Set, AIGroup ): A new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
|
||||
-- * **Destroying** ( Set, AIGroup ): The AI is being destroyed.
|
||||
-- * **Returning** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods. Handle this state to customize the return behaviour of the AI, if any.
|
||||
--
|
||||
-- ### 1.2.2) AI_BALANCER Events
|
||||
--
|
||||
-- * **Monitor** ( Set ): Every 10 seconds, the Monitor event is triggered to monitor the Set.
|
||||
-- * **Spawn** ( Set, ClientName ): Triggers when there is a new AI group to be spawned with ClientName as the name of reference.
|
||||
-- * **Spawned** ( Set, AIGroup ): Triggers when a new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
|
||||
-- * **Destroy** ( Set, AIGroup ): The AI is being destroyed.
|
||||
-- * **Return** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods.
|
||||
--
|
||||
-- ## 1.3) AI_BALANCER spawn interval for replacement AI
|
||||
--
|
||||
-- Use the method @{#AI_BALANCER.InitSpawnInterval}() to set the earliest and latest interval in seconds that is waited until a new replacement AI is spawned.
|
||||
--
|
||||
-- ## 1.4) AI_BALANCER returns AI to Airbases
|
||||
--
|
||||
-- By default, When a human player joins a slot that is AI_BALANCED, the AI group will be destroyed by default.
|
||||
-- However, there are 2 additional options that you can use to customize the destroy behaviour.
|
||||
-- When a human player joins a slot, you can configure to let the AI return to:
|
||||
--
|
||||
-- * @{#AI_BALANCER.ReturnToHomeAirbase}: Returns the AI to the **home** @{Airbase#AIRBASE}.
|
||||
-- * @{#AI_BALANCER.ReturnToNearestAirbases}: Returns the AI to the **nearest friendly** @{Airbase#AIRBASE}.
|
||||
--
|
||||
-- Note that when AI returns to an airbase, the AI_BALANCER will trigger the **Return** event and the AI will return,
|
||||
-- otherwise the AI_BALANCER will trigger a **Destroy** event, and the AI will be destroyed.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **API CHANGE HISTORY**
|
||||
@ -90,12 +50,68 @@
|
||||
--
|
||||
-- @module AI_Balancer
|
||||
|
||||
--- AI_BALANCER class
|
||||
-- @type AI_BALANCER
|
||||
--- @type AI_BALANCER
|
||||
-- @field Core.Set#SET_CLIENT SetClient
|
||||
-- @field Functional.Spawn#SPAWN SpawnAI
|
||||
-- @field Wrapper.Group#GROUP Test
|
||||
-- @extends Core.Fsm#FSM_SET
|
||||
|
||||
|
||||
--- # AI_BALANCER class, extends @{Fsm#FSM_SET}
|
||||
--
|
||||
-- The AI_BALANCER class monitors and manages as many replacement AI groups as there are
|
||||
-- CLIENTS in a SET_CLIENT collection, which are not occupied by human players.
|
||||
-- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions.
|
||||
--
|
||||
-- The parent class @{Fsm#FSM_SET} manages the functionality to control the Finite State Machine (FSM).
|
||||
-- The mission designer can tailor the behaviour of the AI_BALANCER, by defining event and state transition methods.
|
||||
-- An explanation about state and event transition methods can be found in the @{FSM} module documentation.
|
||||
--
|
||||
-- The mission designer can tailor the AI_BALANCER behaviour, by implementing a state or event handling method for the following:
|
||||
--
|
||||
-- * @{#AI_BALANCER.OnAfterSpawned}( AISet, From, Event, To, AIGroup ): Define to add extra logic when an AI is spawned.
|
||||
--
|
||||
-- ## 1. AI_BALANCER construction
|
||||
--
|
||||
-- Create a new AI_BALANCER object with the @{#AI_BALANCER.New}() method:
|
||||
--
|
||||
-- ## 2. AI_BALANCER is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 2.1. AI_BALANCER States
|
||||
--
|
||||
-- * **Monitoring** ( Set ): Monitoring the Set if all AI is spawned for the Clients.
|
||||
-- * **Spawning** ( Set, ClientName ): There is a new AI group spawned with ClientName as the name of reference.
|
||||
-- * **Spawned** ( Set, AIGroup ): A new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
|
||||
-- * **Destroying** ( Set, AIGroup ): The AI is being destroyed.
|
||||
-- * **Returning** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods. Handle this state to customize the return behaviour of the AI, if any.
|
||||
--
|
||||
-- ### 2.2. AI_BALANCER Events
|
||||
--
|
||||
-- * **Monitor** ( Set ): Every 10 seconds, the Monitor event is triggered to monitor the Set.
|
||||
-- * **Spawn** ( Set, ClientName ): Triggers when there is a new AI group to be spawned with ClientName as the name of reference.
|
||||
-- * **Spawned** ( Set, AIGroup ): Triggers when a new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
|
||||
-- * **Destroy** ( Set, AIGroup ): The AI is being destroyed.
|
||||
-- * **Return** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods.
|
||||
--
|
||||
-- ## 3. AI_BALANCER spawn interval for replacement AI
|
||||
--
|
||||
-- Use the method @{#AI_BALANCER.InitSpawnInterval}() to set the earliest and latest interval in seconds that is waited until a new replacement AI is spawned.
|
||||
--
|
||||
-- ## 4. AI_BALANCER returns AI to Airbases
|
||||
--
|
||||
-- By default, When a human player joins a slot that is AI_BALANCED, the AI group will be destroyed by default.
|
||||
-- However, there are 2 additional options that you can use to customize the destroy behaviour.
|
||||
-- When a human player joins a slot, you can configure to let the AI return to:
|
||||
--
|
||||
-- * @{#AI_BALANCER.ReturnToHomeAirbase}: Returns the AI to the **home** @{Airbase#AIRBASE}.
|
||||
-- * @{#AI_BALANCER.ReturnToNearestAirbases}: Returns the AI to the **nearest friendly** @{Airbase#AIRBASE}.
|
||||
--
|
||||
-- Note that when AI returns to an airbase, the AI_BALANCER will trigger the **Return** event and the AI will return,
|
||||
-- otherwise the AI_BALANCER will trigger a **Destroy** event, and the AI will be destroyed.
|
||||
--
|
||||
-- @field #AI_BALANCER
|
||||
AI_BALANCER = {
|
||||
ClassName = "AI_BALANCER",
|
||||
PatrolZones = {},
|
||||
|
||||
@ -11,6 +11,22 @@
|
||||
-- * @{#AI_CAP_ZONE}: Perform a CAP in a zone.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- ### [AI_CAP Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAP%20-%20Combat%20Air%20Patrol)
|
||||
--
|
||||
-- ### [AI_CAP Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAP%20-%20Combat%20Air%20Patrol)
|
||||
--
|
||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ### [AI_CAP YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1YCyPxJgoZn-CfhwyeW65L)
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # **API CHANGE HISTORY**
|
||||
--
|
||||
@ -48,9 +64,9 @@
|
||||
-- @extends AI.AI_Patrol#AI_PATROL_ZONE
|
||||
|
||||
|
||||
--- # 1) @{#AI_CAP_ZONE} class, extends @{AI_CAP#AI_PATROL_ZONE}
|
||||
--- # AI_CAP_ZONE class, extends @{AI_CAP#AI_PATROL_ZONE}
|
||||
--
|
||||
-- The @{#AI_CAP_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}
|
||||
-- The AI_CAP_ZONE class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}
|
||||
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
|
||||
--
|
||||
-- 
|
||||
@ -81,22 +97,22 @@
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ## 1.1) AI_CAP_ZONE constructor
|
||||
-- ## 1. AI_CAP_ZONE constructor
|
||||
--
|
||||
-- * @{#AI_CAP_ZONE.New}(): Creates a new AI_CAP_ZONE object.
|
||||
--
|
||||
-- ## 1.2) AI_CAP_ZONE is a FSM
|
||||
-- ## 2. AI_CAP_ZONE is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 1.2.1) AI_CAP_ZONE States
|
||||
-- ### 2.1 AI_CAP_ZONE States
|
||||
--
|
||||
-- * **None** ( Group ): The process is not started yet.
|
||||
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
|
||||
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
|
||||
-- * **Returning** ( Group ): The AI is returning to Base..
|
||||
--
|
||||
-- ### 1.2.2) AI_CAP_ZONE Events
|
||||
-- ### 2.2 AI_CAP_ZONE Events
|
||||
--
|
||||
-- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
|
||||
-- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
|
||||
@ -109,7 +125,7 @@
|
||||
-- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Unit}s assigned in the CAS task.
|
||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
||||
--
|
||||
-- ## 1.3) Set the Range of Engagement
|
||||
-- ## 3. Set the Range of Engagement
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@ -119,7 +135,7 @@
|
||||
-- The range is applied at the position of the AI.
|
||||
-- Use the method @{AI_CAP#AI_CAP_ZONE.SetEngageRange}() to define that range.
|
||||
--
|
||||
-- ## 1.4) Set the Zone of Engagement
|
||||
-- ## 4. Set the Zone of Engagement
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@ -129,8 +145,7 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_CAP_ZONE AI_CAP_ZONE
|
||||
--
|
||||
-- @field #AI_CAP_ZONE
|
||||
AI_CAP_ZONE = {
|
||||
ClassName = "AI_CAP_ZONE",
|
||||
}
|
||||
|
||||
@ -10,6 +10,22 @@
|
||||
--
|
||||
-- * @{#AI_CAS_ZONE}: Perform a CAS in a zone.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- ### [AI_CAS Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAS%20-%20Close%20Air%20Support)
|
||||
--
|
||||
-- ### [AI_CAS Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAS%20-%20Close%20Air%20Support)
|
||||
--
|
||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ### [AI_CAS YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl3JBO1WDqqpyYRRmIkR2ir2)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **API CHANGE HISTORY**
|
||||
@ -46,11 +62,11 @@
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed.
|
||||
-- @extends AI.AI_Patrol#AI_PATROL_ZONE
|
||||
|
||||
--- # 1) @{#AI_CAS_ZONE} class, extends @{AI_Patrol#AI_PATROL_ZONE}
|
||||
--- # AI_CAS_ZONE class, extends @{AI_Patrol#AI_PATROL_ZONE}
|
||||
--
|
||||
-- @{#AI_CAS_ZONE} derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour.
|
||||
-- AI_CAS_ZONE derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour.
|
||||
--
|
||||
-- The @{#AI_CAS_ZONE} class implements the core functions to provide Close Air Support in an Engage @{Zone} by an AIR @{Controllable} or @{Group}.
|
||||
-- The AI_CAS_ZONE class implements the core functions to provide Close Air Support in an Engage @{Zone} by an AIR @{Controllable} or @{Group}.
|
||||
-- The AI_CAS_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone.
|
||||
--
|
||||
-- 
|
||||
@ -104,22 +120,22 @@
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # 1.1) AI_CAS_ZONE constructor
|
||||
-- # 1. AI_CAS_ZONE constructor
|
||||
--
|
||||
-- * @{#AI_CAS_ZONE.New}(): Creates a new AI_CAS_ZONE object.
|
||||
--
|
||||
-- ## 1.2) AI_CAS_ZONE is a FSM
|
||||
-- ## 2. AI_CAS_ZONE is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 1.2.1) AI_CAS_ZONE States
|
||||
-- ### 2.1. AI_CAS_ZONE States
|
||||
--
|
||||
-- * **None** ( Group ): The process is not started yet.
|
||||
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
|
||||
-- * **Engaging** ( Group ): The AI is engaging the targets in the Engage Zone, executing CAS.
|
||||
-- * **Returning** ( Group ): The AI is returning to Base..
|
||||
--
|
||||
-- ### 1.2.2) AI_CAS_ZONE Events
|
||||
-- ### 2.2. AI_CAS_ZONE Events
|
||||
--
|
||||
-- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
|
||||
-- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
|
||||
@ -134,8 +150,7 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_CAS_ZONE AI_CAS_ZONE
|
||||
--
|
||||
-- @field #AI_CAS_ZONE
|
||||
AI_CAS_ZONE = {
|
||||
ClassName = "AI_CAS_ZONE",
|
||||
}
|
||||
|
||||
@ -12,6 +12,22 @@
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- ### [AI_PATROL Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/PAT%20-%20Patrolling)
|
||||
--
|
||||
-- ### [AI_PATROL Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/PAT%20-%20Patrolling)
|
||||
--
|
||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ### [AI_PATROL YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl35HvYZKA6G22WMt7iI3zky)
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # **OPEN ISSUES**
|
||||
--
|
||||
-- 2017-01-17: When Spawned AI is located at an airbase, it will be routed first back to the airbase after take-off.
|
||||
@ -64,9 +80,9 @@
|
||||
-- @field Functional.Spawn#SPAWN CoordTest
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
--- # 1) @{#AI_PATROL_ZONE} class, extends @{Fsm#FSM_CONTROLLABLE}
|
||||
--- # AI_PATROL_ZONE class, extends @{Fsm#FSM_CONTROLLABLE}
|
||||
--
|
||||
-- The @{#AI_PATROL_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}.
|
||||
-- The AI_PATROL_ZONE class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@ -97,15 +113,15 @@
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ## 1.1) AI_PATROL_ZONE constructor
|
||||
-- ## 1. AI_PATROL_ZONE constructor
|
||||
--
|
||||
-- * @{#AI_PATROL_ZONE.New}(): Creates a new AI_PATROL_ZONE object.
|
||||
--
|
||||
-- ## 1.2) AI_PATROL_ZONE is a FSM
|
||||
-- ## 2. AI_PATROL_ZONE is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 1.2.1) AI_PATROL_ZONE States
|
||||
-- ### 2.1. AI_PATROL_ZONE States
|
||||
--
|
||||
-- * **None** ( Group ): The process is not started yet.
|
||||
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
|
||||
@ -113,7 +129,7 @@
|
||||
-- * **Stopped** ( Group ): The process is stopped.
|
||||
-- * **Crashed** ( Group ): The AI has crashed or is dead.
|
||||
--
|
||||
-- ### 1.2.2) AI_PATROL_ZONE Events
|
||||
-- ### 2.2. AI_PATROL_ZONE Events
|
||||
--
|
||||
-- * **Start** ( Group ): Start the process.
|
||||
-- * **Stop** ( Group ): Stop the process.
|
||||
@ -123,17 +139,17 @@
|
||||
-- * **Detected** ( Group ): The AI has detected new targets.
|
||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
||||
--
|
||||
-- ## 1.3) Set or Get the AI controllable
|
||||
-- ## 3. Set or Get the AI controllable
|
||||
--
|
||||
-- * @{#AI_PATROL_ZONE.SetControllable}(): Set the AIControllable.
|
||||
-- * @{#AI_PATROL_ZONE.GetControllable}(): Get the AIControllable.
|
||||
--
|
||||
-- ## 1.4) Set the Speed and Altitude boundaries of the AI controllable
|
||||
-- ## 4. Set the Speed and Altitude boundaries of the AI controllable
|
||||
--
|
||||
-- * @{#AI_PATROL_ZONE.SetSpeed}(): Set the patrol speed boundaries of the AI, for the next patrol.
|
||||
-- * @{#AI_PATROL_ZONE.SetAltitude}(): Set altitude boundaries of the AI, for the next patrol.
|
||||
--
|
||||
-- ## 1.5) Manage the detection process of the AI controllable
|
||||
-- ## 5. Manage the detection process of the AI controllable
|
||||
--
|
||||
-- The detection process of the AI controllable can be manipulated.
|
||||
-- Detection requires an amount of CPU power, which has an impact on your mission performance.
|
||||
@ -150,7 +166,7 @@
|
||||
-- Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected
|
||||
-- according the weather conditions.
|
||||
--
|
||||
-- ## 1.6) Manage the "out of fuel" in the AI_PATROL_ZONE
|
||||
-- ## 6. Manage the "out of fuel" in the AI_PATROL_ZONE
|
||||
--
|
||||
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
|
||||
@ -159,7 +175,7 @@
|
||||
-- Once the time is finished, the old AI will return to the base.
|
||||
-- Use the method @{#AI_PATROL_ZONE.ManageFuel}() to have this proces in place.
|
||||
--
|
||||
-- ## 1.7) Manage "damage" behaviour of the AI in the AI_PATROL_ZONE
|
||||
-- ## 7. Manage "damage" behaviour of the AI in the AI_PATROL_ZONE
|
||||
--
|
||||
-- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on.
|
||||
-- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB).
|
||||
@ -167,8 +183,7 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_PATROL_ZONE AI_PATROL_ZONE
|
||||
--
|
||||
-- @field #AI_PATROL_ZONE
|
||||
AI_PATROL_ZONE = {
|
||||
ClassName = "AI_PATROL_ZONE",
|
||||
}
|
||||
|
||||
@ -219,16 +219,17 @@ local _ClassID = 0
|
||||
BASE = {
|
||||
ClassName = "BASE",
|
||||
ClassID = 0,
|
||||
_Private = {},
|
||||
Events = {},
|
||||
States = {}
|
||||
States = {},
|
||||
_ = {},
|
||||
}
|
||||
|
||||
--- The Formation Class
|
||||
-- @type FORMATION
|
||||
-- @field Cone A cone formation.
|
||||
FORMATION = {
|
||||
Cone = "Cone"
|
||||
Cone = "Cone",
|
||||
Vee = "Vee"
|
||||
}
|
||||
|
||||
|
||||
@ -360,7 +361,7 @@ do -- Event Handling
|
||||
-- @param #BASE self
|
||||
-- @return #number The @{Event} processing Priority.
|
||||
function BASE:GetEventPriority()
|
||||
return self._Private.EventPriority or 5
|
||||
return self._.EventPriority or 5
|
||||
end
|
||||
|
||||
--- Set the Class @{Event} processing Priority.
|
||||
@ -370,7 +371,7 @@ do -- Event Handling
|
||||
-- @param #number EventPriority The @{Event} processing Priority.
|
||||
-- @return self
|
||||
function BASE:SetEventPriority( EventPriority )
|
||||
self._Private.EventPriority = EventPriority
|
||||
self._.EventPriority = EventPriority
|
||||
end
|
||||
|
||||
--- Remove all subscribed events
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -6,12 +6,14 @@
|
||||
-- ===================================================
|
||||
-- Mission designers can use the DATABASE class to refer to:
|
||||
--
|
||||
-- * STATICS
|
||||
-- * UNITS
|
||||
-- * GROUPS
|
||||
-- * CLIENTS
|
||||
-- * AIRPORTS
|
||||
-- * AIRBASES
|
||||
-- * PLAYERSJOINED
|
||||
-- * PLAYERS
|
||||
-- * CARGOS
|
||||
--
|
||||
-- On top, for internal MOOSE administration purposes, the DATBASE administers the Unit and Group TEMPLATES as defined within the Mission Editor.
|
||||
--
|
||||
@ -44,6 +46,7 @@ DATABASE = {
|
||||
Templates = {
|
||||
Units = {},
|
||||
Groups = {},
|
||||
Statics = {},
|
||||
ClientsByName = {},
|
||||
ClientsByID = {},
|
||||
},
|
||||
@ -53,6 +56,7 @@ DATABASE = {
|
||||
PLAYERS = {},
|
||||
PLAYERSJOINED = {},
|
||||
CLIENTS = {},
|
||||
CARGOS = {},
|
||||
AIRBASES = {},
|
||||
COUNTRY_ID = {},
|
||||
COUNTRY_NAME = {},
|
||||
@ -84,13 +88,15 @@ local _DATABASECategory =
|
||||
function DATABASE:New()
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #DATABASE
|
||||
|
||||
self:SetEventPriority( 1 )
|
||||
|
||||
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
|
||||
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
||||
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
||||
self:HandleEvent( EVENTS.NewCargo )
|
||||
self:HandleEvent( EVENTS.DeleteCargo )
|
||||
|
||||
-- Follow alive players and clients
|
||||
self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit )
|
||||
@ -166,22 +172,24 @@ end
|
||||
|
||||
--- Adds a Airbase based on the Airbase Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:AddAirbase( DCSAirbaseName )
|
||||
-- @param #string AirbaseName The name of the airbase
|
||||
function DATABASE:AddAirbase( AirbaseName )
|
||||
|
||||
if not self.AIRBASES[DCSAirbaseName] then
|
||||
self.AIRBASES[DCSAirbaseName] = AIRBASE:Register( DCSAirbaseName )
|
||||
if not self.AIRBASES[AirbaseName] then
|
||||
self.AIRBASES[AirbaseName] = AIRBASE:Register( AirbaseName )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Deletes a Airbase from the DATABASE based on the Airbase Name.
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:DeleteAirbase( DCSAirbaseName )
|
||||
-- @param #string AirbaseName The name of the airbase
|
||||
function DATABASE:DeleteAirbase( AirbaseName )
|
||||
|
||||
--self.AIRBASES[DCSAirbaseName] = nil
|
||||
self.AIRBASES[AirbaseName] = nil
|
||||
end
|
||||
|
||||
--- Finds a AIRBASE based on the AirbaseName.
|
||||
--- Finds an AIRBASE based on the AirbaseName.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string AirbaseName
|
||||
-- @return Wrapper.Airbase#AIRBASE The found AIRBASE.
|
||||
@ -191,6 +199,35 @@ function DATABASE:FindAirbase( AirbaseName )
|
||||
return AirbaseFound
|
||||
end
|
||||
|
||||
--- Adds a Cargo based on the Cargo Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string CargoName The name of the airbase
|
||||
function DATABASE:AddCargo( Cargo )
|
||||
|
||||
if not self.CARGOS[Cargo.Name] then
|
||||
self.CARGOS[Cargo.Name] = Cargo
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Deletes a Cargo from the DATABASE based on the Cargo Name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string CargoName The name of the airbase
|
||||
function DATABASE:DeleteCargo( CargoName )
|
||||
|
||||
self.CARGOS[CargoName] = nil
|
||||
end
|
||||
|
||||
--- Finds an CARGO based on the CargoName.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string CargoName
|
||||
-- @return Wrapper.Cargo#CARGO The found CARGO.
|
||||
function DATABASE:FindCargo( CargoName )
|
||||
|
||||
local CargoFound = self.CARGOS[CargoName]
|
||||
return CargoFound
|
||||
end
|
||||
|
||||
|
||||
--- Finds a CLIENT based on the ClientName.
|
||||
-- @param #DATABASE self
|
||||
@ -282,7 +319,7 @@ function DATABASE:Spawn( SpawnTemplate )
|
||||
SpawnTemplate.CountryID = nil
|
||||
SpawnTemplate.CategoryID = nil
|
||||
|
||||
self:_RegisterTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID )
|
||||
self:_RegisterGroupTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID )
|
||||
|
||||
self:T3( SpawnTemplate )
|
||||
coalition.addGroup( SpawnCountryID, SpawnCategoryID, SpawnTemplate )
|
||||
@ -318,7 +355,7 @@ end
|
||||
-- @param #DATABASE self
|
||||
-- @param #table GroupTemplate
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_RegisterTemplate( GroupTemplate, CoalitionID, CategoryID, CountryID )
|
||||
function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID, CountryID )
|
||||
|
||||
local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name)
|
||||
|
||||
@ -396,6 +433,54 @@ function DATABASE:GetGroupTemplate( GroupName )
|
||||
return GroupTemplate
|
||||
end
|
||||
|
||||
--- Private method that registers new Static Templates within the DATABASE Object.
|
||||
-- @param #DATABASE self
|
||||
-- @param #table GroupTemplate
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID )
|
||||
|
||||
local TraceTable = {}
|
||||
|
||||
local StaticTemplateName = env.getValueDictByKey(StaticTemplate.name)
|
||||
|
||||
self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {}
|
||||
|
||||
StaticTemplate.CategoryID = CategoryID
|
||||
StaticTemplate.CoalitionID = CoalitionID
|
||||
StaticTemplate.CountryID = CountryID
|
||||
|
||||
self.Templates.Statics[StaticTemplateName].StaticName = StaticTemplateName
|
||||
self.Templates.Statics[StaticTemplateName].GroupTemplate = StaticTemplate
|
||||
self.Templates.Statics[StaticTemplateName].UnitTemplate = StaticTemplate.units[1]
|
||||
self.Templates.Statics[StaticTemplateName].CategoryID = CategoryID
|
||||
self.Templates.Statics[StaticTemplateName].CoalitionID = CoalitionID
|
||||
self.Templates.Statics[StaticTemplateName].CountryID = CountryID
|
||||
|
||||
|
||||
TraceTable[#TraceTable+1] = "Static"
|
||||
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].GroupName
|
||||
|
||||
TraceTable[#TraceTable+1] = "Coalition"
|
||||
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CoalitionID
|
||||
TraceTable[#TraceTable+1] = "Category"
|
||||
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CategoryID
|
||||
TraceTable[#TraceTable+1] = "Country"
|
||||
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CountryID
|
||||
|
||||
self:E( TraceTable )
|
||||
end
|
||||
|
||||
|
||||
--- @param #DATABASE self
|
||||
function DATABASE:GetStaticUnitTemplate( StaticName )
|
||||
local StaticTemplate = self.Templates.Statics[StaticName].UnitTemplate
|
||||
StaticTemplate.SpawnCoalitionID = self.Templates.Statics[StaticName].CoalitionID
|
||||
StaticTemplate.SpawnCategoryID = self.Templates.Statics[StaticName].CategoryID
|
||||
StaticTemplate.SpawnCountryID = self.Templates.Statics[StaticName].CountryID
|
||||
return StaticTemplate
|
||||
end
|
||||
|
||||
|
||||
function DATABASE:GetGroupNameFromUnitName( UnitName )
|
||||
return self.Templates.Units[UnitName].GroupName
|
||||
end
|
||||
@ -665,7 +750,7 @@ end
|
||||
|
||||
--- Iterate the DATABASE and call an iterator function for each **alive** UNIT, providing the UNIT and optional parameters.
|
||||
-- @param #DATABASE self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the database. The function needs to accept a UNIT parameter.
|
||||
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a UNIT parameter.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachUnit( IteratorFunction, FinalizeFunction, ... )
|
||||
self:F2( arg )
|
||||
@ -677,7 +762,7 @@ end
|
||||
|
||||
--- Iterate the DATABASE and call an iterator function for each **alive** GROUP, providing the GROUP and optional parameters.
|
||||
-- @param #DATABASE self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the database. The function needs to accept a GROUP parameter.
|
||||
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a GROUP parameter.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachGroup( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
@ -690,7 +775,7 @@ end
|
||||
|
||||
--- Iterate the DATABASE and call an iterator function for each **ALIVE** player, providing the player name and optional parameters.
|
||||
-- @param #DATABASE self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an player in the database. The function needs to accept the player name.
|
||||
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept the player name.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachPlayer( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
@ -703,7 +788,7 @@ end
|
||||
|
||||
--- Iterate the DATABASE and call an iterator function for each player who has joined the mission, providing the Unit of the player and optional parameters.
|
||||
-- @param #DATABASE self
|
||||
-- @param #function IteratorFunction The function that will be called when there is was a player in the database. The function needs to accept a UNIT parameter.
|
||||
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a UNIT parameter.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachPlayerJoined( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
@ -715,7 +800,7 @@ end
|
||||
|
||||
--- Iterate the DATABASE and call an iterator function for each CLIENT, providing the CLIENT to the function and optional parameters.
|
||||
-- @param #DATABASE self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive player in the database. The function needs to accept a CLIENT parameter.
|
||||
-- @param #function IteratorFunction The function that will be called object in the database. The function needs to accept a CLIENT parameter.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachClient( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
@ -725,7 +810,44 @@ function DATABASE:ForEachClient( IteratorFunction, ... )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Iterate the DATABASE and call an iterator function for each CARGO, providing the CARGO object to the function and optional parameters.
|
||||
-- @param #DATABASE self
|
||||
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a CLIENT parameter.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachCargo( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
self:ForEach( IteratorFunction, arg, self.CARGOS )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Handles the OnEventNewCargo event.
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function DATABASE:OnEventNewCargo( EventData )
|
||||
self:F2( { EventData } )
|
||||
|
||||
if EventData.Cargo then
|
||||
self:AddCargo( EventData.Cargo )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Handles the OnEventDeleteCargo.
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function DATABASE:OnEventDeleteCargo( EventData )
|
||||
self:F2( { EventData } )
|
||||
|
||||
if EventData.Cargo then
|
||||
self:DeleteCargo( EventData.Cargo.Name )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @param #DATABASE self
|
||||
function DATABASE:_RegisterTemplates()
|
||||
self:F2()
|
||||
|
||||
@ -781,11 +903,18 @@ function DATABASE:_RegisterTemplates()
|
||||
|
||||
--self.Units[coa_name][countryName][category] = {}
|
||||
|
||||
for group_num, GroupTemplate in pairs(obj_type_data.group) do
|
||||
for group_num, Template in pairs(obj_type_data.group) do
|
||||
|
||||
if GroupTemplate and GroupTemplate.units and type(GroupTemplate.units) == 'table' then --making sure again- this is a valid group
|
||||
self:_RegisterTemplate(
|
||||
GroupTemplate,
|
||||
if obj_type_name ~= "static" and Template and Template.units and type(Template.units) == 'table' then --making sure again- this is a valid group
|
||||
self:_RegisterGroupTemplate(
|
||||
Template,
|
||||
CoalitionSide,
|
||||
_DATABASECategory[string.lower(CategoryName)],
|
||||
CountryID
|
||||
)
|
||||
else
|
||||
self:_RegisterStaticTemplate(
|
||||
Template,
|
||||
CoalitionSide,
|
||||
_DATABASECategory[string.lower(CategoryName)],
|
||||
CountryID
|
||||
|
||||
@ -197,6 +197,9 @@ EVENT = {
|
||||
ClassID = 0,
|
||||
}
|
||||
|
||||
world.event.S_EVENT_NEW_CARGO = world.event.S_EVENT_MAX + 1000
|
||||
world.event.S_EVENT_DELETE_CARGO = world.event.S_EVENT_MAX + 1001
|
||||
|
||||
--- The different types of events supported by MOOSE.
|
||||
-- Use this structure to subscribe to events using the @{Base#BASE.HandleEvent}() method.
|
||||
-- @type EVENTS
|
||||
@ -224,13 +227,15 @@ EVENTS = {
|
||||
PlayerComment = world.event.S_EVENT_PLAYER_COMMENT,
|
||||
ShootingStart = world.event.S_EVENT_SHOOTING_START,
|
||||
ShootingEnd = world.event.S_EVENT_SHOOTING_END,
|
||||
NewCargo = world.event.S_EVENT_NEW_CARGO,
|
||||
DeleteCargo = world.event.S_EVENT_DELETE_CARGO,
|
||||
}
|
||||
|
||||
--- The Event structure
|
||||
-- Note that at the beginning of each field description, there is an indication which field will be populated depending on the object type involved in the Event:
|
||||
--
|
||||
-- * A (Object.Category.)UNIT : A UNIT object type is involved in the Event.
|
||||
-- * A (Object.Category.)STATIC : A STATIC object type is involved in the Event.µ
|
||||
-- * A (Object.Category.)STATIC : A STATIC object type is involved in the Event.µ
|
||||
--
|
||||
-- @type EVENTDATA
|
||||
-- @field #number id The identifier of the event.
|
||||
@ -271,122 +276,156 @@ EVENTS = {
|
||||
-- @field WeaponTgtDCSUnit
|
||||
|
||||
|
||||
|
||||
local _EVENTMETA = {
|
||||
[world.event.S_EVENT_SHOT] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventShot",
|
||||
Text = "S_EVENT_SHOT"
|
||||
},
|
||||
[world.event.S_EVENT_HIT] = {
|
||||
Order = 1,
|
||||
Side = "T",
|
||||
Event = "OnEventHit",
|
||||
Text = "S_EVENT_HIT"
|
||||
},
|
||||
[world.event.S_EVENT_TAKEOFF] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventTakeoff",
|
||||
Text = "S_EVENT_TAKEOFF"
|
||||
},
|
||||
[world.event.S_EVENT_LAND] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventLand",
|
||||
Text = "S_EVENT_LAND"
|
||||
},
|
||||
[world.event.S_EVENT_CRASH] = {
|
||||
Order = -1,
|
||||
Side = "I",
|
||||
Event = "OnEventCrash",
|
||||
Text = "S_EVENT_CRASH"
|
||||
},
|
||||
[world.event.S_EVENT_EJECTION] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventEjection",
|
||||
Text = "S_EVENT_EJECTION"
|
||||
},
|
||||
[world.event.S_EVENT_REFUELING] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventRefueling",
|
||||
Text = "S_EVENT_REFUELING"
|
||||
},
|
||||
[world.event.S_EVENT_DEAD] = {
|
||||
Order = -1,
|
||||
Side = "I",
|
||||
Event = "OnEventDead",
|
||||
Text = "S_EVENT_DEAD"
|
||||
},
|
||||
[world.event.S_EVENT_PILOT_DEAD] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventPilotDead",
|
||||
Text = "S_EVENT_PILOT_DEAD"
|
||||
},
|
||||
[world.event.S_EVENT_BASE_CAPTURED] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventBaseCaptured",
|
||||
Text = "S_EVENT_BASE_CAPTURED"
|
||||
},
|
||||
[world.event.S_EVENT_MISSION_START] = {
|
||||
Order = 1,
|
||||
Side = "N",
|
||||
Event = "OnEventMissionStart",
|
||||
Text = "S_EVENT_MISSION_START"
|
||||
},
|
||||
[world.event.S_EVENT_MISSION_END] = {
|
||||
Order = 1,
|
||||
Side = "N",
|
||||
Event = "OnEventMissionEnd",
|
||||
Text = "S_EVENT_MISSION_END"
|
||||
},
|
||||
[world.event.S_EVENT_TOOK_CONTROL] = {
|
||||
Order = 1,
|
||||
Side = "N",
|
||||
Event = "OnEventTookControl",
|
||||
Text = "S_EVENT_TOOK_CONTROL"
|
||||
},
|
||||
[world.event.S_EVENT_REFUELING_STOP] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventRefuelingStop",
|
||||
Text = "S_EVENT_REFUELING_STOP"
|
||||
},
|
||||
[world.event.S_EVENT_BIRTH] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventBirth",
|
||||
Text = "S_EVENT_BIRTH"
|
||||
},
|
||||
[world.event.S_EVENT_HUMAN_FAILURE] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventHumanFailure",
|
||||
Text = "S_EVENT_HUMAN_FAILURE"
|
||||
},
|
||||
[world.event.S_EVENT_ENGINE_STARTUP] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventEngineStartup",
|
||||
Text = "S_EVENT_ENGINE_STARTUP"
|
||||
},
|
||||
[world.event.S_EVENT_ENGINE_SHUTDOWN] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventEngineShutdown",
|
||||
Text = "S_EVENT_ENGINE_SHUTDOWN"
|
||||
},
|
||||
[world.event.S_EVENT_PLAYER_ENTER_UNIT] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventPlayerEnterUnit",
|
||||
Text = "S_EVENT_PLAYER_ENTER_UNIT"
|
||||
},
|
||||
[world.event.S_EVENT_PLAYER_LEAVE_UNIT] = {
|
||||
Order = -1,
|
||||
Side = "I",
|
||||
Event = "OnEventPlayerLeaveUnit",
|
||||
Text = "S_EVENT_PLAYER_LEAVE_UNIT"
|
||||
},
|
||||
[world.event.S_EVENT_PLAYER_COMMENT] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventPlayerComment",
|
||||
Text = "S_EVENT_PLAYER_COMMENT"
|
||||
},
|
||||
[world.event.S_EVENT_SHOOTING_START] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventShootingStart",
|
||||
Text = "S_EVENT_SHOOTING_START"
|
||||
},
|
||||
[world.event.S_EVENT_SHOOTING_END] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventShootingEnd",
|
||||
Text = "S_EVENT_SHOOTING_END"
|
||||
},
|
||||
[EVENTS.NewCargo] = {
|
||||
Order = 1,
|
||||
Event = "OnEventNewCargo",
|
||||
Text = "S_EVENT_NEW_CARGO"
|
||||
},
|
||||
[EVENTS.DeleteCargo] = {
|
||||
Order = 1,
|
||||
Event = "OnEventDeleteCargo",
|
||||
Text = "S_EVENT_DELETE_CARGO"
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -401,13 +440,6 @@ function EVENT:New()
|
||||
return self
|
||||
end
|
||||
|
||||
function EVENT:EventText( EventID )
|
||||
|
||||
local EventText = _EVENTMETA[EventID].Text
|
||||
|
||||
return EventText
|
||||
end
|
||||
|
||||
|
||||
--- Initializes the Events structure for the event
|
||||
-- @param #EVENT self
|
||||
@ -419,7 +451,7 @@ function EVENT:Init( EventID, EventClass )
|
||||
|
||||
if not self.Events[EventID] then
|
||||
-- Create a WEAK table to ensure that the garbage collector is cleaning the event links when the object usage is cleaned.
|
||||
self.Events[EventID] = setmetatable( {}, { __mode = "k" } )
|
||||
self.Events[EventID] = {}
|
||||
end
|
||||
|
||||
-- Each event has a subtable of EventClasses, ordered by EventPriority.
|
||||
@ -429,53 +461,56 @@ function EVENT:Init( EventID, EventClass )
|
||||
end
|
||||
|
||||
if not self.Events[EventID][EventPriority][EventClass] then
|
||||
self.Events[EventID][EventPriority][EventClass] = setmetatable( {}, { __mode = "v" } )
|
||||
self.Events[EventID][EventPriority][EventClass] = {}
|
||||
end
|
||||
return self.Events[EventID][EventPriority][EventClass]
|
||||
end
|
||||
|
||||
--- Removes an Events entry
|
||||
--- Removes a subscription
|
||||
-- @param #EVENT self
|
||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||
-- @param Dcs.DCSWorld#world.event EventID
|
||||
-- @return #EVENT.Events
|
||||
function EVENT:Remove( EventClass, EventID )
|
||||
self:F3( { EventClass, _EVENTMETA[EventID].Text } )
|
||||
|
||||
local EventClass = EventClass
|
||||
self:E( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
|
||||
|
||||
local EventPriority = EventClass:GetEventPriority()
|
||||
|
||||
self.EventsDead = self.EventsDead or {}
|
||||
self.EventsDead[EventID] = self.EventsDead[EventID] or {}
|
||||
self.EventsDead[EventID][EventPriority] = self.EventsDead[EventID][EventPriority] or {}
|
||||
self.EventsDead[EventID][EventPriority][EventClass] = self.Events[EventID][EventPriority][EventClass]
|
||||
|
||||
self.Events[EventID][EventPriority][EventClass] = nil
|
||||
|
||||
end
|
||||
|
||||
--- Removes an Events entry for a UNIT.
|
||||
--- Resets subscriptions
|
||||
-- @param #EVENT self
|
||||
-- @param #string UnitName The name of the UNIT.
|
||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||
-- @param Dcs.DCSWorld#world.event EventID
|
||||
-- @return #EVENT.Events
|
||||
function EVENT:RemoveForUnit( UnitName, EventClass, EventID )
|
||||
self:F3( { EventClass, _EVENTMETA[EventID].Text } )
|
||||
function EVENT:Reset( EventObject )
|
||||
|
||||
local EventClass = EventClass
|
||||
local EventPriority = EventClass:GetEventPriority()
|
||||
local Event = self.Events[EventID][EventPriority][EventClass]
|
||||
Event.EventUnit[UnitName] = nil
|
||||
self:E( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } )
|
||||
|
||||
local EventPriority = EventObject:GetEventPriority()
|
||||
for EventID, EventData in pairs( self.Events ) do
|
||||
if self.EventsDead then
|
||||
if self.EventsDead[EventID] then
|
||||
if self.EventsDead[EventID][EventPriority] then
|
||||
if self.EventsDead[EventID][EventPriority][EventObject] then
|
||||
self.Events[EventID][EventPriority][EventObject] = self.EventsDead[EventID][EventPriority][EventObject]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Removes an Events entry for a GROUP.
|
||||
-- @param #EVENT self
|
||||
-- @param #string GroupName The name of the GROUP.
|
||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||
-- @param Dcs.DCSWorld#world.event EventID
|
||||
-- @return #EVENT.Events
|
||||
function EVENT:RemoveForGroup( GroupName, EventClass, EventID )
|
||||
self:F3( { EventClass, _EVENTMETA[EventID].Text } )
|
||||
|
||||
local EventClass = EventClass
|
||||
local EventPriority = EventClass:GetEventPriority()
|
||||
local Event = self.Events[EventID][EventPriority][EventClass]
|
||||
Event.EventGroup[GroupName] = nil
|
||||
end
|
||||
|
||||
|
||||
--- Clears all event subscriptions for a @{Base#BASE} derived object.
|
||||
-- @param #EVENT self
|
||||
@ -519,7 +554,6 @@ function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
|
||||
|
||||
local EventData = self:Init( EventID, EventClass )
|
||||
EventData.EventFunction = EventFunction
|
||||
EventData.EventClass = EventClass
|
||||
|
||||
return self
|
||||
end
|
||||
@ -536,12 +570,8 @@ function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID )
|
||||
self:F2( UnitName )
|
||||
|
||||
local EventData = self:Init( EventID, EventClass )
|
||||
if not EventData.EventUnit then
|
||||
EventData.EventUnit = {}
|
||||
end
|
||||
EventData.EventUnit[UnitName] = {}
|
||||
EventData.EventUnit[UnitName].EventFunction = EventFunction
|
||||
EventData.EventUnit[UnitName].EventClass = EventClass
|
||||
EventData.EventUnit = true
|
||||
EventData.EventFunction = EventFunction
|
||||
return self
|
||||
end
|
||||
|
||||
@ -556,12 +586,8 @@ function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID )
|
||||
self:F2( GroupName )
|
||||
|
||||
local Event = self:Init( EventID, EventClass )
|
||||
if not Event.EventGroup then
|
||||
Event.EventGroup = {}
|
||||
end
|
||||
Event.EventGroup[GroupName] = {}
|
||||
Event.EventGroup[GroupName].EventFunction = EventFunction
|
||||
Event.EventGroup[GroupName].EventClass = EventClass
|
||||
Event.EventGroup = true
|
||||
Event.EventFunction = EventFunction
|
||||
return self
|
||||
end
|
||||
|
||||
@ -672,6 +698,39 @@ do -- OnEngineShutDown
|
||||
|
||||
end
|
||||
|
||||
do -- Event Creation
|
||||
|
||||
--- Creation of a New Cargo Event.
|
||||
-- @param #EVENT self
|
||||
-- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created.
|
||||
function EVENT:CreateEventNewCargo( Cargo )
|
||||
self:F( { Cargo } )
|
||||
|
||||
local Event = {
|
||||
id = EVENTS.NewCargo,
|
||||
time = timer.getTime(),
|
||||
cargo = Cargo,
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a Cargo Deletion Event.
|
||||
-- @param #EVENT self
|
||||
-- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created.
|
||||
function EVENT:CreateEventDeleteCargo( Cargo )
|
||||
self:F( { Cargo } )
|
||||
|
||||
local Event = {
|
||||
id = EVENTS.DeleteCargo,
|
||||
time = timer.getTime(),
|
||||
cargo = Cargo,
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #EVENT self
|
||||
-- @param #EVENTDATA Event
|
||||
@ -687,10 +746,10 @@ function EVENT:onEvent( Event )
|
||||
return errmsg
|
||||
end
|
||||
|
||||
self:E( _EVENTMETA[Event.id].Text, Event )
|
||||
|
||||
local EventMeta = _EVENTMETA[Event.id]
|
||||
|
||||
if self and self.Events and self.Events[Event.id] then
|
||||
|
||||
|
||||
if Event.initiator then
|
||||
|
||||
@ -795,12 +854,17 @@ function EVENT:onEvent( Event )
|
||||
--Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
|
||||
end
|
||||
|
||||
local PriorityOrder = _EVENTMETA[Event.id].Order
|
||||
if Event.cargo then
|
||||
Event.Cargo = Event.cargo
|
||||
Event.CargoName = Event.cargo.Name
|
||||
end
|
||||
|
||||
local PriorityOrder = EventMeta.Order
|
||||
local PriorityBegin = PriorityOrder == -1 and 5 or 1
|
||||
local PriorityEnd = PriorityOrder == -1 and 1 or 5
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { _EVENTMETA[Event.id].Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } )
|
||||
self:E( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } )
|
||||
end
|
||||
|
||||
for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do
|
||||
@ -810,186 +874,144 @@ function EVENT:onEvent( Event )
|
||||
-- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called.
|
||||
for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do
|
||||
|
||||
self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
|
||||
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
|
||||
-- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT.
|
||||
if ( Event.IniDCSUnitName and EventData.EventUnit and EventData.EventUnit[Event.IniDCSUnitName] ) or
|
||||
( Event.TgtDCSUnitName and EventData.EventUnit and EventData.EventUnit[Event.TgtDCSUnitName] ) then
|
||||
if EventData.EventUnit then
|
||||
|
||||
if EventData.EventUnit[Event.IniDCSUnitName] then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventUnit[Event.IniDCSUnitName].EventFunction then
|
||||
-- So now the EventClass must be a UNIT class!!! We check if it is still "Alive".
|
||||
if EventClass:IsAlive() or
|
||||
Event.id == EVENTS.Crash or
|
||||
Event.id == EVENTS.Dead then
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventUnit[Event.IniDCSUnitName].EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
local UnitName = EventClass:GetName()
|
||||
|
||||
if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or
|
||||
( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event )
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if EventData.EventUnit[Event.TgtDCSUnitName] then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventUnit[Event.TgtDCSUnitName].EventFunction then
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventUnit[Event.TgtDCSUnitName].EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ EventMeta.Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
||||
self:Remove( EventClass, Event.id )
|
||||
end
|
||||
else
|
||||
|
||||
-- If the EventData is for a GROUP, the call directly the EventClass EventFunction for the UNIT in that GROUP.
|
||||
if ( Event.IniDCSUnitName and Event.IniDCSGroupName and Event.IniGroupName and EventData.EventGroup and EventData.EventGroup[Event.IniGroupName] ) or
|
||||
( Event.TgtDCSUnitName and Event.TgtDCSGroupName and Event.TgtGroupName and EventData.EventGroup and EventData.EventGroup[Event.TgtGroupName] ) then
|
||||
if EventData.EventGroup then
|
||||
|
||||
if EventData.EventGroup[Event.IniGroupName] then
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventGroup[Event.IniGroupName].EventFunction then
|
||||
-- So now the EventClass must be a GROUP class!!! We check if it is still "Alive".
|
||||
if EventClass:IsAlive() or
|
||||
Event.id == EVENTS.Crash or
|
||||
Event.id == EVENTS.Dead then
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventGroup[Event.IniGroupName].EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
end
|
||||
-- We can get the name of the EventClass, which is now always a GROUP object.
|
||||
local GroupName = EventClass:GetName()
|
||||
|
||||
if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or
|
||||
( EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName ) then
|
||||
|
||||
if EventData.EventGroup[Event.TgtGroupName] then
|
||||
if EventData.EventGroup[Event.TgtGroupName].EventFunction then
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventGroup[Event.TgtGroupName].EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- If the EventData is not bound to a specific unit, then call the EventClass EventFunction.
|
||||
-- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon.
|
||||
if (Event.IniDCSUnit or Event.WeaponUNIT) and not EventData.EventUnit then
|
||||
|
||||
if EventClass == EventData.EventClass then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
-- There is an EventFunction defined, so call the EventFunction.
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ]
|
||||
local EventFunction = EventClass[ EventMeta.Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
self:E( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
local Result, Value = EventFunction( EventClass, Event )
|
||||
return Result, Value
|
||||
return EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
||||
self:Remove( EventClass, Event.id )
|
||||
end
|
||||
else
|
||||
|
||||
-- If the EventData is not bound to a specific unit, then call the EventClass EventFunction.
|
||||
-- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon.
|
||||
if not EventData.EventUnit then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
-- There is an EventFunction defined, so call the EventFunction.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ EventMeta.Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
local Result, Value = EventFunction( EventClass, Event )
|
||||
return Result, Value
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -997,7 +1019,7 @@ function EVENT:onEvent( Event )
|
||||
end
|
||||
end
|
||||
else
|
||||
self:E( { _EVENTMETA[Event.id].Text, Event } )
|
||||
self:E( { EventMeta.Text, Event } )
|
||||
end
|
||||
|
||||
Event = nil
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- A Finite State Machine (FSM) models a process flow that transitions between various **States** through triggered **Events**.
|
||||
--
|
||||
-- A FSM can only be in one of a finite number of states.
|
||||
-- The machine is in only one state at a time; the state it is in at any given time is called the **current state**.
|
||||
-- It can change from one state to another when initiated by an **__internal__ or __external__ triggering event**, which is called a **transition**.
|
||||
@ -90,8 +92,43 @@ do -- FSM
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- # 1) FSM class, extends @{Base#BASE}
|
||||
--- # FSM class, extends @{Base#BASE}
|
||||
--
|
||||
-- A Finite State Machine (FSM) models a process flow that transitions between various **States** through triggered **Events**.
|
||||
--
|
||||
-- A FSM can only be in one of a finite number of states.
|
||||
-- The machine is in only one state at a time; the state it is in at any given time is called the **current state**.
|
||||
-- It can change from one state to another when initiated by an **__internal__ or __external__ triggering event**, which is called a **transition**.
|
||||
-- An **FSM implementation** is defined by **a list of its states**, **its initial state**, and **the triggering events** for **each possible transition**.
|
||||
-- An FSM implementation is composed out of **two parts**, a set of **state transition rules**, and an implementation set of **state transition handlers**, implementing those transitions.
|
||||
--
|
||||
-- The FSM class supports a **hierarchical implementation of a Finite State Machine**,
|
||||
-- that is, it allows to **embed existing FSM implementations in a master FSM**.
|
||||
-- FSM hierarchies allow for efficient FSM re-use, **not having to re-invent the wheel every time again** when designing complex processes.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The above diagram shows a graphical representation of a FSM implementation for a **Task**, which guides a Human towards a Zone,
|
||||
-- orders him to destroy x targets and account the results.
|
||||
-- Other examples of ready made FSM could be:
|
||||
--
|
||||
-- * route a plane to a zone flown by a human
|
||||
-- * detect targets by an AI and report to humans
|
||||
-- * account for destroyed targets by human players
|
||||
-- * handle AI infantry to deploy from or embark to a helicopter or airplane or vehicle
|
||||
-- * let an AI patrol a zone
|
||||
--
|
||||
-- The **MOOSE framework** uses extensively the FSM class and derived FSM\_ classes,
|
||||
-- because **the goal of MOOSE is to simplify mission design complexity for mission building**.
|
||||
-- By efficiently utilizing the FSM class and derived classes, MOOSE allows mission designers to quickly build processes.
|
||||
-- **Ready made FSM-based implementations classes** exist within the MOOSE framework that **can easily be re-used,
|
||||
-- and tailored** by mission designers through **the implementation of Transition Handlers**.
|
||||
-- Each of these FSM implementation classes start either with:
|
||||
--
|
||||
-- * an acronym **AI\_**, which indicates an FSM implementation directing **AI controlled** @{GROUP} and/or @{UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class.
|
||||
-- * an acronym **TASK\_**, which indicates an FSM implementation executing a @{TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class.
|
||||
-- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{TASK}, seated in a @{CLIENT} (slot) or a @{UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The FSM class is the base class of all FSM\_ derived classes. It implements the main functionality to define and execute Finite State Machines.
|
||||
@ -114,13 +151,13 @@ do -- FSM
|
||||
-- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation.
|
||||
-- The below documentation has a seperate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**.
|
||||
--
|
||||
-- ## 1.1) FSM Linear Transitions
|
||||
-- ## FSM Linear Transitions
|
||||
--
|
||||
-- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**.
|
||||
-- The Lineair transition rule evaluation will always be done from the **current state** of the FSM.
|
||||
-- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop.
|
||||
--
|
||||
-- ### 1.1.1) FSM Transition Rules
|
||||
-- ### FSM Transition Rules
|
||||
--
|
||||
-- The FSM has transition rules that it follows and validates, as it walks the process.
|
||||
-- These rules define when an FSM can transition from a specific state towards an other specific state upon a triggered event.
|
||||
@ -145,7 +182,7 @@ do -- FSM
|
||||
-- * It can be switched **Off** by triggering event **SwitchOff**.
|
||||
-- * Note that once the Switch is **On** or **Middle**, it can only be switched **Off**.
|
||||
--
|
||||
-- ### Some additional comments:
|
||||
-- #### Some additional comments:
|
||||
--
|
||||
-- Note that Linear Transition Rules **can be declared in a few variations**:
|
||||
--
|
||||
@ -156,7 +193,7 @@ do -- FSM
|
||||
--
|
||||
-- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" )
|
||||
--
|
||||
-- ### 1.1.2) Transition Handling
|
||||
-- ### Transition Handling
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@ -178,7 +215,7 @@ do -- FSM
|
||||
--
|
||||
-- On top, each of these methods can have a variable amount of parameters passed. See the example in section [1.1.3](#1.1.3\)-event-triggers).
|
||||
--
|
||||
-- ### 1.1.3) Event Triggers
|
||||
-- ### Event Triggers
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@ -216,7 +253,7 @@ do -- FSM
|
||||
--
|
||||
-- Because ... When Event was asynchronously processed after 5 seconds, Amount was set to 2. So be careful when processing and passing values and objects in asynchronous processing!
|
||||
--
|
||||
-- ### 1.1.4) Linear Transition Example
|
||||
-- ### Linear Transition Example
|
||||
--
|
||||
-- This example is fully implemented in the MOOSE test mission on GITHUB: [FSM-100 - Transition Explanation](https://github.com/FlightControl-Master/MOOSE/blob/master/Moose%20Test%20Missions/FSM%20-%20Finite%20State%20Machine/FSM-100%20-%20Transition%20Explanation/FSM-100%20-%20Transition%20Explanation.lua)
|
||||
--
|
||||
@ -298,7 +335,7 @@ do -- FSM
|
||||
-- So... When FsmDemo:Stop() is being triggered, the state of FsmDemo will transition from Red or Green to Stopped.
|
||||
-- And there is no transition handling method defined for that transition, thus, no new event is being triggered causing the FsmDemo process flow to halt.
|
||||
--
|
||||
-- ## 1.5) FSM Hierarchical Transitions
|
||||
-- ## FSM Hierarchical Transitions
|
||||
--
|
||||
-- Hierarchical Transitions allow to re-use readily available and implemented FSMs.
|
||||
-- This becomes in very useful for mission building, where mission designers build complex processes and workflows,
|
||||
|
||||
@ -27,109 +27,26 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- The above menus classes **are derived** from 2 main **abstract** classes defined within the MOOSE framework (so don't use these):
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
--
|
||||
-- 1) MENU_ BASE abstract base classes (don't use them)
|
||||
-- ====================================================
|
||||
-- The underlying base menu classes are **NOT** to be used within your missions.
|
||||
-- These are simply abstract base classes defining a couple of fields that are used by the
|
||||
-- derived MENU_ classes to manage menus.
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- 1.1) @{#MENU_BASE} class, extends @{Base#BASE}
|
||||
-- --------------------------------------------------
|
||||
-- The @{#MENU_BASE} class defines the main MENU class where other MENU classes are derived from.
|
||||
--
|
||||
-- 1.2) @{#MENU_COMMAND_BASE} class, extends @{Base#BASE}
|
||||
-- ----------------------------------------------------------
|
||||
-- The @{#MENU_COMMAND_BASE} class defines the main MENU class where other MENU COMMAND_ classes are derived from, in order to set commands.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- **The next menus define the MENU classes that you can use within your missions.**
|
||||
--
|
||||
-- 2) MENU MISSION classes
|
||||
-- ======================
|
||||
-- The underlying classes manage the menus for a complete mission file.
|
||||
--
|
||||
-- 2.1) @{#MENU_MISSION} class, extends @{Menu#MENU_BASE}
|
||||
-- ---------------------------------------------------------
|
||||
-- The @{Menu#MENU_MISSION} class manages the main menus for a complete mission.
|
||||
-- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}.
|
||||
--
|
||||
-- 2.2) @{#MENU_MISSION_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE}
|
||||
-- -------------------------------------------------------------------------
|
||||
-- The @{Menu#MENU_MISSION_COMMAND} class manages the command menus for a complete mission, which allow players to execute functions during mission execution.
|
||||
-- You can add menus with the @{#MENU_MISSION_COMMAND.New} method, which constructs a MENU_MISSION_COMMAND object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION_COMMAND.Remove}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 3) MENU COALITION classes
|
||||
-- =========================
|
||||
-- The underlying classes manage the menus for whole coalitions.
|
||||
--
|
||||
-- 3.1) @{#MENU_COALITION} class, extends @{Menu#MENU_BASE}
|
||||
-- ------------------------------------------------------------
|
||||
-- The @{Menu#MENU_COALITION} class manages the main menus for coalitions.
|
||||
-- You can add menus with the @{#MENU_COALITION.New} method, which constructs a MENU_COALITION object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION.Remove}.
|
||||
--
|
||||
-- 3.2) @{Menu#MENU_COALITION_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE}
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- The @{Menu#MENU_COALITION_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution.
|
||||
-- You can add menus with the @{#MENU_COALITION_COMMAND.New} method, which constructs a MENU_COALITION_COMMAND object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION_COMMAND.Remove}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 4) MENU GROUP classes
|
||||
-- =====================
|
||||
-- The underlying classes manage the menus for groups. Note that groups can be inactive, alive or can be destroyed.
|
||||
--
|
||||
-- 4.1) @{Menu#MENU_GROUP} class, extends @{Menu#MENU_BASE}
|
||||
-- --------------------------------------------------------
|
||||
-- The @{Menu#MENU_GROUP} class manages the main menus for coalitions.
|
||||
-- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}.
|
||||
--
|
||||
-- 4.2) @{Menu#MENU_GROUP_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE}
|
||||
-- ------------------------------------------------------------------------
|
||||
-- The @{Menu#MENU_GROUP_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution.
|
||||
-- You can add menus with the @{#MENU_GROUP_COMMAND.New} method, which constructs a MENU_GROUP_COMMAND object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP_COMMAND.Remove}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 5) MENU CLIENT classes
|
||||
-- ======================
|
||||
-- The underlying classes manage the menus for units with skill level client or player.
|
||||
--
|
||||
-- 5.1) @{Menu#MENU_CLIENT} class, extends @{Menu#MENU_BASE}
|
||||
-- ---------------------------------------------------------
|
||||
-- The @{Menu#MENU_CLIENT} class manages the main menus for coalitions.
|
||||
-- You can add menus with the @{#MENU_CLIENT.New} method, which constructs a MENU_CLIENT object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT.Remove}.
|
||||
--
|
||||
-- 5.2) @{Menu#MENU_CLIENT_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE}
|
||||
-- -------------------------------------------------------------------------
|
||||
-- The @{Menu#MENU_CLIENT_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution.
|
||||
-- You can add menus with the @{#MENU_CLIENT_COMMAND.New} method, which constructs a MENU_CLIENT_COMMAND object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT_COMMAND.Remove}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Contributions: -
|
||||
-- ### Authors: FlightControl : Design & Programming
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Design & Programming
|
||||
--
|
||||
-- @module Menu
|
||||
|
||||
|
||||
do -- MENU_BASE
|
||||
|
||||
--- The MENU_BASE class
|
||||
-- @type MENU_BASE
|
||||
--- @type MENU_BASE
|
||||
-- @extends Base#BASE
|
||||
|
||||
--- # MENU_BASE class, extends @{Base#BASE}
|
||||
-- The MENU_BASE class defines the main MENU class where other MENU classes are derived from.
|
||||
-- This is an abstract class, so don't use it.
|
||||
-- @field #MENU_BASE
|
||||
MENU_BASE = {
|
||||
ClassName = "MENU_BASE",
|
||||
MenuPath = nil,
|
||||
@ -193,10 +110,15 @@ end
|
||||
|
||||
do -- MENU_COMMAND_BASE
|
||||
|
||||
--- The MENU_COMMAND_BASE class
|
||||
-- @type MENU_COMMAND_BASE
|
||||
--- @type MENU_COMMAND_BASE
|
||||
-- @field #function MenuCallHandler
|
||||
-- @extends Core.Menu#MENU_BASE
|
||||
|
||||
--- # MENU_COMMAND_BASE class, extends @{Base#BASE}
|
||||
-- ----------------------------------------------------------
|
||||
-- The MENU_COMMAND_BASE class defines the main MENU class where other MENU COMMAND_
|
||||
-- classes are derived from, in order to set commands.
|
||||
-- @field #MENU_COMMAND_BASE
|
||||
MENU_COMMAND_BASE = {
|
||||
ClassName = "MENU_COMMAND_BASE",
|
||||
CommandMenuFunction = nil,
|
||||
@ -224,9 +146,15 @@ end
|
||||
|
||||
do -- MENU_MISSION
|
||||
|
||||
--- The MENU_MISSION class
|
||||
-- @type MENU_MISSION
|
||||
--- @type MENU_MISSION
|
||||
-- @extends Core.Menu#MENU_BASE
|
||||
|
||||
--- # MENU_MISSION class, extends @{Menu#MENU_BASE}
|
||||
--
|
||||
-- The MENU_MISSION class manages the main menus for a complete mission.
|
||||
-- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}.
|
||||
-- @field #MENU_MISSION
|
||||
MENU_MISSION = {
|
||||
ClassName = "MENU_MISSION"
|
||||
}
|
||||
@ -291,9 +219,16 @@ end
|
||||
|
||||
do -- MENU_MISSION_COMMAND
|
||||
|
||||
--- The MENU_MISSION_COMMAND class
|
||||
-- @type MENU_MISSION_COMMAND
|
||||
--- @type MENU_MISSION_COMMAND
|
||||
-- @extends Core.Menu#MENU_COMMAND_BASE
|
||||
|
||||
--- # MENU_MISSION_COMMAND class, extends @{Menu#MENU_COMMAND_BASE}
|
||||
--
|
||||
-- The MENU_MISSION_COMMAND class manages the command menus for a complete mission, which allow players to execute functions during mission execution.
|
||||
-- You can add menus with the @{#MENU_MISSION_COMMAND.New} method, which constructs a MENU_MISSION_COMMAND object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION_COMMAND.Remove}.
|
||||
--
|
||||
-- @field #MENU_MISSION_COMMAND
|
||||
MENU_MISSION_COMMAND = {
|
||||
ClassName = "MENU_MISSION_COMMAND"
|
||||
}
|
||||
@ -341,9 +276,16 @@ end
|
||||
|
||||
do -- MENU_COALITION
|
||||
|
||||
--- The MENU_COALITION class
|
||||
-- @type MENU_COALITION
|
||||
--- @type MENU_COALITION
|
||||
-- @extends Core.Menu#MENU_BASE
|
||||
|
||||
--- # MENU_COALITION class, extends @{Menu#MENU_BASE}
|
||||
--
|
||||
-- The @{Menu#MENU_COALITION} class manages the main menus for coalitions.
|
||||
-- You can add menus with the @{#MENU_COALITION.New} method, which constructs a MENU_COALITION object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION.Remove}.
|
||||
--
|
||||
--
|
||||
-- @usage
|
||||
-- -- This demo creates a menu structure for the planes within the red coalition.
|
||||
-- -- To test, join the planes, then look at the other radio menus (Option F10).
|
||||
@ -380,6 +322,8 @@ do -- MENU_COALITION
|
||||
--
|
||||
-- local MenuAdd = MENU_COALITION_COMMAND:New( coalition.side.RED, "Add Status Menu", MenuCoalitionRed, AddStatusMenu )
|
||||
-- local MenuRemove = MENU_COALITION_COMMAND:New( coalition.side.RED, "Remove Status Menu", MenuCoalitionRed, RemoveStatusMenu )
|
||||
--
|
||||
-- @field #MENU_COALITION
|
||||
MENU_COALITION = {
|
||||
ClassName = "MENU_COALITION"
|
||||
}
|
||||
@ -446,9 +390,16 @@ end
|
||||
|
||||
do -- MENU_COALITION_COMMAND
|
||||
|
||||
--- The MENU_COALITION_COMMAND class
|
||||
-- @type MENU_COALITION_COMMAND
|
||||
--- @type MENU_COALITION_COMMAND
|
||||
-- @extends Core.Menu#MENU_COMMAND_BASE
|
||||
|
||||
--- # MENU_COALITION_COMMAND class, extends @{Menu#MENU_COMMAND_BASE}
|
||||
--
|
||||
-- The MENU_COALITION_COMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution.
|
||||
-- You can add menus with the @{#MENU_COALITION_COMMAND.New} method, which constructs a MENU_COALITION_COMMAND object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION_COMMAND.Remove}.
|
||||
--
|
||||
-- @field #MENU_COALITION_COMMAND
|
||||
MENU_COALITION_COMMAND = {
|
||||
ClassName = "MENU_COALITION_COMMAND"
|
||||
}
|
||||
@ -506,6 +457,14 @@ do -- MENU_CLIENT
|
||||
--- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters.
|
||||
-- @type MENU_CLIENT
|
||||
-- @extends Core.Menu#MENU_BASE
|
||||
|
||||
|
||||
--- # MENU_CLIENT class, extends @{Menu#MENU_BASE}
|
||||
--
|
||||
-- The MENU_CLIENT class manages the main menus for coalitions.
|
||||
-- You can add menus with the @{#MENU_CLIENT.New} method, which constructs a MENU_CLIENT object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT.Remove}.
|
||||
--
|
||||
-- @usage
|
||||
-- -- This demo creates a menu structure for the two clients of planes.
|
||||
-- -- Each client will receive a different menu structure.
|
||||
@ -555,6 +514,8 @@ do -- MENU_CLIENT
|
||||
-- MENU_CLIENT_COMMAND:New( PlaneClient, "Remove Status Menu Plane 2", MenuManage, RemoveStatusMenu, PlaneClient )
|
||||
-- end
|
||||
-- end, {}, 10, 10 )
|
||||
--
|
||||
-- @field #MENU_CLIENT
|
||||
MENU_CLIENT = {
|
||||
ClassName = "MENU_CLIENT"
|
||||
}
|
||||
@ -644,9 +605,16 @@ do -- MENU_CLIENT
|
||||
end
|
||||
|
||||
|
||||
--- The MENU_CLIENT_COMMAND class
|
||||
-- @type MENU_CLIENT_COMMAND
|
||||
--- @type MENU_CLIENT_COMMAND
|
||||
-- @extends Core.Menu#MENU_COMMAND
|
||||
|
||||
--- # MENU_CLIENT_COMMAND class, extends @{Menu#MENU_COMMAND_BASE}
|
||||
--
|
||||
-- The MENU_CLIENT_COMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution.
|
||||
-- You can add menus with the @{#MENU_CLIENT_COMMAND.New} method, which constructs a MENU_CLIENT_COMMAND object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT_COMMAND.Remove}.
|
||||
--
|
||||
-- @field #MENU_CLIENT_COMMAND
|
||||
MENU_CLIENT_COMMAND = {
|
||||
ClassName = "MENU_CLIENT_COMMAND"
|
||||
}
|
||||
@ -730,9 +698,16 @@ do
|
||||
-- These menu classes are handling this logic with this variable.
|
||||
local _MENUGROUPS = {}
|
||||
|
||||
--- The MENU_GROUP class
|
||||
-- @type MENU_GROUP
|
||||
--- @type MENU_GROUP
|
||||
-- @extends Core.Menu#MENU_BASE
|
||||
|
||||
|
||||
--- #MENU_GROUP class, extends @{Menu#MENU_BASE}
|
||||
--
|
||||
-- The MENU_GROUP class manages the main menus for coalitions.
|
||||
-- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}.
|
||||
--
|
||||
-- @usage
|
||||
-- -- This demo creates a menu structure for the two groups of planes.
|
||||
-- -- Each group will receive a different menu structure.
|
||||
@ -783,6 +758,7 @@ do
|
||||
-- end
|
||||
-- end, {}, 10, 10 )
|
||||
--
|
||||
-- @field #MENU_GROUP
|
||||
MENU_GROUP = {
|
||||
ClassName = "MENU_GROUP"
|
||||
}
|
||||
@ -876,9 +852,16 @@ do
|
||||
end
|
||||
|
||||
|
||||
--- The MENU_GROUP_COMMAND class
|
||||
-- @type MENU_GROUP_COMMAND
|
||||
--- @type MENU_GROUP_COMMAND
|
||||
-- @extends Core.Menu#MENU_BASE
|
||||
|
||||
--- # MENU_GROUP_COMMAND class, extends @{Menu#MENU_COMMAND_BASE}
|
||||
--
|
||||
-- The @{Menu#MENU_GROUP_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution.
|
||||
-- You can add menus with the @{#MENU_GROUP_COMMAND.New} method, which constructs a MENU_GROUP_COMMAND object and returns you the object reference.
|
||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP_COMMAND.Remove}.
|
||||
--
|
||||
-- @field #MENU_GROUP_COMMAND
|
||||
MENU_GROUP_COMMAND = {
|
||||
ClassName = "MENU_GROUP_COMMAND"
|
||||
}
|
||||
|
||||
@ -4,18 +4,24 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # 1) @{Message#MESSAGE} class, extends @{Base#BASE}
|
||||
-- @module Message
|
||||
|
||||
--- The MESSAGE class
|
||||
-- @type MESSAGE
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- # MESSAGE class, extends @{Base#BASE}
|
||||
--
|
||||
-- Message System to display Messages to Clients, Coalitions or All.
|
||||
-- Messages are shown on the display panel for an amount of seconds, and will then disappear.
|
||||
-- Messages can contain a category which is indicating the category of the message.
|
||||
--
|
||||
-- ## 1.1) MESSAGE construction
|
||||
-- ## MESSAGE construction
|
||||
--
|
||||
-- Messages are created with @{Message#MESSAGE.New}. Note that when the MESSAGE object is created, no message is sent yet.
|
||||
-- To send messages, you need to use the To functions.
|
||||
--
|
||||
-- ## 1.2) Send messages to an audience
|
||||
-- ## Send messages to an audience
|
||||
--
|
||||
-- Messages are sent:
|
||||
--
|
||||
@ -26,19 +32,14 @@
|
||||
-- * To the blue coalition using @{Message#MESSAGE.ToBlue}().
|
||||
-- * To all Players using @{Message#MESSAGE.ToAll}().
|
||||
--
|
||||
-- ## 1.3) Send conditionally to an audience
|
||||
-- ## Send conditionally to an audience
|
||||
--
|
||||
-- Messages can be sent conditionally to an audience (when a condition is true):
|
||||
--
|
||||
-- * To all players using @{Message#MESSAGE.ToAllIf}().
|
||||
-- * To a coalition using @{Message#MESSAGE.ToCoalitionIf}().
|
||||
--
|
||||
--
|
||||
-- @module Message
|
||||
|
||||
--- The MESSAGE class
|
||||
-- @type MESSAGE
|
||||
-- @extends Core.Base#BASE
|
||||
-- @field #MESSAGE
|
||||
MESSAGE = {
|
||||
ClassName = "MESSAGE",
|
||||
MessageCategory = 0,
|
||||
|
||||
@ -1,91 +1,22 @@
|
||||
--- **Core** - **POINT\_VEC** classes define an **extensive API** to **manage 3D points** in the simulation space.
|
||||
--
|
||||
-- 1) @{Point#POINT_VEC3} class, extends @{Base#BASE}
|
||||
-- ==================================================
|
||||
-- The @{Point#POINT_VEC3} class defines a 3D point in the simulator.
|
||||
--
|
||||
-- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts.
|
||||
-- In order to keep the credibility of the the author, I want to emphasize that the of the MIST framework was created by Grimes, who you can find on the Eagle Dynamics Forums.
|
||||
--
|
||||
-- ## 1.1) POINT_VEC3 constructor
|
||||
--
|
||||
-- A new POINT_VEC3 instance can be created with:
|
||||
--
|
||||
-- * @{Point#POINT_VEC3.New}(): a 3D point.
|
||||
-- * @{Point#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCSTypes#Vec3}.
|
||||
--
|
||||
-- ## 1.2) Manupulate the X, Y, Z coordinates of the point
|
||||
--
|
||||
-- A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate.
|
||||
-- Methods exist to manupulate these coordinates.
|
||||
--
|
||||
-- The current X, Y, Z axis can be retrieved with the methods @{#POINT_VEC3.GetX}(), @{#POINT_VEC3.GetY}(), @{#POINT_VEC3.GetZ}() respectively.
|
||||
-- The methods @{#POINT_VEC3.SetX}(), @{#POINT_VEC3.SetY}(), @{#POINT_VEC3.SetZ}() change the respective axis with a new value.
|
||||
-- The current axis values can be changed by using the methods @{#POINT_VEC3.AddX}(), @{#POINT_VEC3.AddY}(), @{#POINT_VEC3.AddZ}()
|
||||
-- to add or substract a value from the current respective axis value.
|
||||
-- Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example:
|
||||
--
|
||||
-- local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3()
|
||||
--
|
||||
-- ## 1.3) Create waypoints for routes
|
||||
--
|
||||
-- A POINT_VEC3 can prepare waypoints for Ground, Air and Naval groups to be embedded into a Route.
|
||||
--
|
||||
--
|
||||
-- ## 1.5) Smoke, flare, explode, illuminate
|
||||
--
|
||||
-- At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods:
|
||||
--
|
||||
-- ### 1.5.1) Smoke
|
||||
--
|
||||
-- * @{#POINT_VEC3.Smoke}(): To smoke the point in a certain color.
|
||||
-- * @{#POINT_VEC3.SmokeBlue}(): To smoke the point in blue.
|
||||
-- * @{#POINT_VEC3.SmokeRed}(): To smoke the point in red.
|
||||
-- * @{#POINT_VEC3.SmokeOrange}(): To smoke the point in orange.
|
||||
-- * @{#POINT_VEC3.SmokeWhite}(): To smoke the point in white.
|
||||
-- * @{#POINT_VEC3.SmokeGreen}(): To smoke the point in green.
|
||||
--
|
||||
-- ### 1.5.2) Flare
|
||||
--
|
||||
-- * @{#POINT_VEC3.Flare}(): To flare the point in a certain color.
|
||||
-- * @{#POINT_VEC3.FlareRed}(): To flare the point in red.
|
||||
-- * @{#POINT_VEC3.FlareYellow}(): To flare the point in yellow.
|
||||
-- * @{#POINT_VEC3.FlareWhite}(): To flare the point in white.
|
||||
-- * @{#POINT_VEC3.FlareGreen}(): To flare the point in green.
|
||||
--
|
||||
-- ### 1.5.3) Explode
|
||||
--
|
||||
-- * @{#POINT_VEC3.Explosion}(): To explode the point with a certain intensity.
|
||||
--
|
||||
-- ### 1.5.4) Illuminate
|
||||
--
|
||||
-- * @{#POINT_VEC3.IlluminationBomb}(): To illuminate the point.
|
||||
--
|
||||
--
|
||||
-- 2) @{Point#POINT_VEC2} class, extends @{Point#POINT_VEC3}
|
||||
-- =========================================================
|
||||
-- The @{Point#POINT_VEC2} class defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified.
|
||||
-- 
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- 2.1) POINT_VEC2 constructor
|
||||
-- ---------------------------
|
||||
-- A new POINT_VEC2 instance can be created with:
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- * @{Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter.
|
||||
-- * @{Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}.
|
||||
-- ### [POINT_VEC Demo Missions source code]()
|
||||
--
|
||||
-- ## 1.2) Manupulate the X, Altitude, Y coordinates of the 2D point
|
||||
-- ### [POINT_VEC Demo Missions, only for beta testers]()
|
||||
--
|
||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||
--
|
||||
-- A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate.
|
||||
-- Methods exist to manupulate these coordinates.
|
||||
-- ====
|
||||
--
|
||||
-- The current X, Altitude, Y axis can be retrieved with the methods @{#POINT_VEC2.GetX}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetY}() respectively.
|
||||
-- The methods @{#POINT_VEC2.SetX}(), @{#POINT_VEC2.SetAlt}(), @{#POINT_VEC2.SetY}() change the respective axis with a new value.
|
||||
-- The current Lat(itude), Alt(itude), Lon(gitude) values can also be retrieved with the methods @{#POINT_VEC2.GetLat}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetLon}() respectively.
|
||||
-- The current axis values can be changed by using the methods @{#POINT_VEC2.AddX}(), @{#POINT_VEC2.AddAlt}(), @{#POINT_VEC2.AddY}()
|
||||
-- to add or substract a value from the current respective axis value.
|
||||
-- Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example:
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2()
|
||||
-- ### [POINT_VEC YouTube Channel]()
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@ -133,6 +64,126 @@
|
||||
-- @field #POINT_VEC3.RoutePointType RoutePointType
|
||||
-- @field #POINT_VEC3.RoutePointAction RoutePointAction
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- # POINT_VEC3 class, extends @{Base#BASE}
|
||||
--
|
||||
-- POINT_VEC3 defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space.
|
||||
--
|
||||
-- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts.
|
||||
-- In order to keep the credibility of the the author,
|
||||
-- I want to emphasize that the formulas embedded in the MIST framework were created by Grimes or previous authors,
|
||||
-- who you can find on the Eagle Dynamics Forums.
|
||||
--
|
||||
--
|
||||
-- ## POINT_VEC3 constructor
|
||||
--
|
||||
-- A new POINT_VEC3 object can be created with:
|
||||
--
|
||||
-- * @{#POINT_VEC3.New}(): a 3D point.
|
||||
-- * @{#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCSTypes#Vec3}.
|
||||
--
|
||||
--
|
||||
-- ## Manupulate the X, Y, Z coordinates of the POINT_VEC3
|
||||
--
|
||||
-- A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate.
|
||||
-- Methods exist to manupulate these coordinates.
|
||||
--
|
||||
-- The current X, Y, Z axis can be retrieved with the methods @{#POINT_VEC3.GetX}(), @{#POINT_VEC3.GetY}(), @{#POINT_VEC3.GetZ}() respectively.
|
||||
-- The methods @{#POINT_VEC3.SetX}(), @{#POINT_VEC3.SetY}(), @{#POINT_VEC3.SetZ}() change the respective axis with a new value.
|
||||
-- The current axis values can be changed by using the methods @{#POINT_VEC3.AddX}(), @{#POINT_VEC3.AddY}(), @{#POINT_VEC3.AddZ}()
|
||||
-- to add or substract a value from the current respective axis value.
|
||||
-- Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example:
|
||||
--
|
||||
-- local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3()
|
||||
--
|
||||
--
|
||||
-- ## Create waypoints for routes
|
||||
--
|
||||
-- A POINT_VEC3 can prepare waypoints for Ground and Air groups to be embedded into a Route.
|
||||
--
|
||||
-- * @{#POINT_VEC3.RoutePointAir}(): Build an air route point.
|
||||
-- * @{#POINT_VEC3.RoutePointGround}(): Build a ground route point.
|
||||
--
|
||||
-- Route points can be used in the Route methods of the @{Group#GROUP} class.
|
||||
--
|
||||
--
|
||||
-- ## Smoke, flare, explode, illuminate
|
||||
--
|
||||
-- At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods:
|
||||
--
|
||||
-- ### Smoke
|
||||
--
|
||||
-- * @{#POINT_VEC3.Smoke}(): To smoke the point in a certain color.
|
||||
-- * @{#POINT_VEC3.SmokeBlue}(): To smoke the point in blue.
|
||||
-- * @{#POINT_VEC3.SmokeRed}(): To smoke the point in red.
|
||||
-- * @{#POINT_VEC3.SmokeOrange}(): To smoke the point in orange.
|
||||
-- * @{#POINT_VEC3.SmokeWhite}(): To smoke the point in white.
|
||||
-- * @{#POINT_VEC3.SmokeGreen}(): To smoke the point in green.
|
||||
--
|
||||
-- ### Flare
|
||||
--
|
||||
-- * @{#POINT_VEC3.Flare}(): To flare the point in a certain color.
|
||||
-- * @{#POINT_VEC3.FlareRed}(): To flare the point in red.
|
||||
-- * @{#POINT_VEC3.FlareYellow}(): To flare the point in yellow.
|
||||
-- * @{#POINT_VEC3.FlareWhite}(): To flare the point in white.
|
||||
-- * @{#POINT_VEC3.FlareGreen}(): To flare the point in green.
|
||||
--
|
||||
-- ### Explode
|
||||
--
|
||||
-- * @{#POINT_VEC3.Explosion}(): To explode the point with a certain intensity.
|
||||
--
|
||||
-- ### Illuminate
|
||||
--
|
||||
-- * @{#POINT_VEC3.IlluminationBomb}(): To illuminate the point.
|
||||
--
|
||||
--
|
||||
-- ## 3D calculation methods
|
||||
--
|
||||
-- Various calculation methods exist to use or manipulate 3D space. Find below a short description of each method:
|
||||
--
|
||||
-- ### Distance
|
||||
--
|
||||
-- * @{#POINT_VEC3.Get3DDistance}(): Obtain the distance from the current 3D point to the provided 3D point in 3D space.
|
||||
-- * @{#POINT_VEC3.Get2DDistance}(): Obtain the distance from the current 3D point to the provided 3D point in 2D space.
|
||||
--
|
||||
-- ### Angle
|
||||
--
|
||||
-- * @{#POINT_VEC3.GetAngleDegrees}(): Obtain the angle in degrees from the current 3D point with the provided 3D direction vector.
|
||||
-- * @{#POINT_VEC3.GetAngleRadians}(): Obtain the angle in radians from the current 3D point with the provided 3D direction vector.
|
||||
-- * @{#POINT_VEC3.GetDirectionVec3}(): Obtain the 3D direction vector from the current 3D point to the provided 3D point.
|
||||
--
|
||||
-- ### Translation
|
||||
--
|
||||
-- * @{#POINT_VEC3.Translate}(): Translate the current 3D point towards an other 3D point using the given Distance and Angle.
|
||||
--
|
||||
-- ### Get the North correction of the current location
|
||||
--
|
||||
-- * @{#POINT_VEC3.GetNorthCorrection}(): Obtains the north correction at the current 3D point.
|
||||
--
|
||||
--
|
||||
-- ## Point Randomization
|
||||
--
|
||||
-- Various methods exist to calculate random locations around a given 3D point.
|
||||
--
|
||||
-- * @{#POINT_VEC3.GetRandomPointVec2InRadius}(): Provides a random 2D point around the current 3D point, in the given inner to outer band.
|
||||
-- * @{#POINT_VEC3.GetRandomPointVec3InRadius}(): Provides a random 3D point around the current 3D point, in the given inner to outer band.
|
||||
-- * @{#POINT_VEC3.GetRandomVec2InRadius}(): Provides a random 2D vector around the current 3D point, in the given inner to outer band.
|
||||
-- * @{#POINT_VEC3.GetRandomVec3InRadius}(): Provides a random 3D vector around the current 3D point, in the given inner to outer band.
|
||||
--
|
||||
--
|
||||
-- ## Metric system
|
||||
--
|
||||
-- * @{#POINT_VEC3.IsMetric}(): Returns if the 3D point is Metric or Nautical Miles.
|
||||
-- * @{#POINT_VEC3.SetMetric}(): Sets the 3D point to Metric or Nautical Miles.
|
||||
--
|
||||
--
|
||||
-- ## Coorinate text generation
|
||||
--
|
||||
-- * @{#POINT_VEC3.ToStringBR}(): Generates a Bearing & Range text in the format of DDD for DI where DDD is degrees and DI is distance.
|
||||
-- * @{#POINT_VEC3.ToStringLL}(): Generates a Latutude & Longutude text.
|
||||
--
|
||||
-- @field #POINT_VEC3 POINT_VEC3
|
||||
--
|
||||
POINT_VEC3 = {
|
||||
ClassName = "POINT_VEC3",
|
||||
Metric = true,
|
||||
@ -149,11 +200,38 @@ POINT_VEC3 = {
|
||||
},
|
||||
}
|
||||
|
||||
--- The POINT_VEC2 class
|
||||
-- @type POINT_VEC2
|
||||
--- @type POINT_VEC2
|
||||
-- @field Dcs.DCSTypes#Distance x The x coordinate in meters.
|
||||
-- @field Dcs.DCSTypes#Distance y the y coordinate in meters.
|
||||
-- @extends Core.Point#POINT_VEC3
|
||||
|
||||
--- # POINT_VEC2 class, extends @{Point#POINT_VEC3}
|
||||
--
|
||||
-- The @{Point#POINT_VEC2} class defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified.
|
||||
--
|
||||
-- ## POINT_VEC2 constructor
|
||||
--
|
||||
-- A new POINT_VEC2 instance can be created with:
|
||||
--
|
||||
-- * @{Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter.
|
||||
-- * @{Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}.
|
||||
--
|
||||
-- ## Manupulate the X, Altitude, Y coordinates of the 2D point
|
||||
--
|
||||
-- A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate.
|
||||
-- Methods exist to manupulate these coordinates.
|
||||
--
|
||||
-- The current X, Altitude, Y axis can be retrieved with the methods @{#POINT_VEC2.GetX}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetY}() respectively.
|
||||
-- The methods @{#POINT_VEC2.SetX}(), @{#POINT_VEC2.SetAlt}(), @{#POINT_VEC2.SetY}() change the respective axis with a new value.
|
||||
-- The current Lat(itude), Alt(itude), Lon(gitude) values can also be retrieved with the methods @{#POINT_VEC2.GetLat}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetLon}() respectively.
|
||||
-- The current axis values can be changed by using the methods @{#POINT_VEC2.AddX}(), @{#POINT_VEC2.AddAlt}(), @{#POINT_VEC2.AddY}()
|
||||
-- to add or substract a value from the current respective axis value.
|
||||
-- Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example:
|
||||
--
|
||||
-- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2()
|
||||
--
|
||||
-- @field #POINT_VEC2 POINT_VEC2
|
||||
--
|
||||
POINT_VEC2 = {
|
||||
ClassName = "POINT_VEC2",
|
||||
}
|
||||
@ -399,11 +477,11 @@ function POINT_VEC3:GetNorthCorrectionRadians()
|
||||
end
|
||||
|
||||
|
||||
--- Return a direction in radians from the POINT_VEC3 using a direction vector in Vec3 format.
|
||||
--- Return an angle in radians from the POINT_VEC3 using a direction vector in Vec3 format.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @param Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format.
|
||||
-- @return #number DirectionRadians The direction in radians.
|
||||
function POINT_VEC3:GetDirectionRadians( DirectionVec3 )
|
||||
-- @return #number DirectionRadians The angle in radians.
|
||||
function POINT_VEC3:GetAngleRadians( DirectionVec3 )
|
||||
local DirectionRadians = math.atan2( DirectionVec3.z, DirectionVec3.x )
|
||||
--DirectionRadians = DirectionRadians + self:GetNorthCorrectionRadians()
|
||||
if DirectionRadians < 0 then
|
||||
@ -412,6 +490,17 @@ function POINT_VEC3:GetDirectionRadians( DirectionVec3 )
|
||||
return DirectionRadians
|
||||
end
|
||||
|
||||
--- Return an angle in degrees from the POINT_VEC3 using a direction vector in Vec3 format.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @param Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format.
|
||||
-- @return #number DirectionRadians The angle in degrees.
|
||||
function POINT_VEC3:GetAngleDegrees( DirectionVec3 )
|
||||
local AngleRadians = self:GetAngleRadians(DirectionVec3)
|
||||
local Angle = UTILS.ToDegree( AngleRadians )
|
||||
return Angle
|
||||
end
|
||||
|
||||
|
||||
--- Return the 2D distance in meters between the target POINT_VEC3 and the POINT_VEC3.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @param #POINT_VEC3 TargetPointVec3 The target POINT_VEC3.
|
||||
@ -482,7 +571,7 @@ end
|
||||
-- @return #string The BR text.
|
||||
function POINT_VEC3:GetBRText( TargetPointVec3 )
|
||||
local DirectionVec3 = self:GetDirectionVec3( TargetPointVec3 )
|
||||
local AngleRadians = self:GetDirectionRadians( DirectionVec3 )
|
||||
local AngleRadians = self:GetAngleRadians( DirectionVec3 )
|
||||
local Distance = self:Get2DDistance( TargetPointVec3 )
|
||||
return self:ToStringBR( AngleRadians, Distance )
|
||||
end
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
--- **Core** - The RADIO class is responsible for **transmitting radio communications**.
|
||||
--
|
||||
-- --- bitmap
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@ -27,19 +27,20 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Authors: Hugues "Grey_Echo" Bousquet
|
||||
-- ### Author: Hugues "Grey_Echo" Bousquet
|
||||
--
|
||||
-- @module Radio
|
||||
|
||||
|
||||
--- # 1) RADIO class, extends @{Base#BASE}
|
||||
--
|
||||
-- ## 1.1) RADIO usage
|
||||
--
|
||||
-- There are 3 steps to a successful radio transmission.
|
||||
--
|
||||
-- * First, you need to **"add" a @{#RADIO} object** to your @{Positionable#POSITIONABLE}. This is done using the @{Positionable#POSITIONABLE.GetRadio}() function,
|
||||
-- * First, you need to **"add a @{#RADIO} object** to your @{Positionable#POSITIONABLE}. This is done using the @{Positionable#POSITIONABLE.GetRadio}() function,
|
||||
-- * Then, you will **set the relevant parameters** to the transmission (see below),
|
||||
-- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{Positionable#POSITIONABLE.Broadcast}() function.
|
||||
-- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{RADIO.Broadcast}() function.
|
||||
--
|
||||
-- Methods to set relevant parameters for both a @{Unit#UNIT} or a @{Group#GROUP} or any other @{Positionable#POSITIONABLE}
|
||||
--
|
||||
@ -53,7 +54,7 @@
|
||||
-- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration,
|
||||
-- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call
|
||||
--
|
||||
-- Additional Methods to set relevant parameters if the transmiter is any other @{Wrapper.Positionable#POSITIONABLE}
|
||||
-- Additional Methods to set relevant parameters if the transmiter is any other @{Positionable#POSITIONABLE}
|
||||
--
|
||||
-- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts
|
||||
-- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call
|
||||
@ -68,7 +69,7 @@
|
||||
-- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission.
|
||||
--
|
||||
-- @type RADIO
|
||||
-- @field Wrapper.Positionable#POSITIONABLE Positionable The transmiter
|
||||
-- @field Positionable#POSITIONABLE Positionable The transmiter
|
||||
-- @field #string FileName Name of the sound file
|
||||
-- @field #number Frequency Frequency of the transmission in Hz
|
||||
-- @field #number Modulation Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM)
|
||||
|
||||
@ -4,31 +4,30 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # 1) @{Scheduler#SCHEDULER} class, extends @{Base#BASE}
|
||||
-- SCHEDULER manages the **scheduling of functions**:
|
||||
--
|
||||
-- The @{Scheduler#SCHEDULER} class creates schedule.
|
||||
--
|
||||
-- ## 1.1) SCHEDULER constructor
|
||||
--
|
||||
-- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters:
|
||||
--
|
||||
-- * @{Scheduler#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection.
|
||||
-- * @{Scheduler#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection.
|
||||
-- * @{Scheduler#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
|
||||
-- * @{Scheduler#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
|
||||
--
|
||||
-- ## 1.2) SCHEDULER timer stopping and (re-)starting.
|
||||
--
|
||||
-- The SCHEDULER can be stopped and restarted with the following methods:
|
||||
--
|
||||
-- * @{Scheduler#SCHEDULER.Start}(): (Re-)Start the schedules within the SCHEDULER object. If a CallID is provided to :Start(), only the schedule referenced by CallID will be (re-)started.
|
||||
-- * @{Scheduler#SCHEDULER.Stop}(): Stop the schedules within the SCHEDULER object. If a CallID is provided to :Stop(), then only the schedule referenced by CallID will be stopped.
|
||||
--
|
||||
-- ## 1.3) Create a new schedule
|
||||
--
|
||||
-- With @{Scheduler#SCHEDULER.Schedule}() a new time event can be scheduled. This function is used by the :New() constructor when a new schedule is planned.
|
||||
-- * optionally in an optional specified time interval,
|
||||
-- * optionally **repeating** with a specified time repeat interval,
|
||||
-- * optionally **randomizing** with a specified time interval randomization factor,
|
||||
-- * optionally **stop** the repeating after a specified time interval.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SCH%20-%20Scheduler)
|
||||
--
|
||||
-- ### [SCHEDULER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler)
|
||||
--
|
||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ### [SCHEDULER YouTube Channel (none)]()
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
@ -38,10 +37,6 @@
|
||||
--
|
||||
-- * FlightControl : Design & Programming
|
||||
--
|
||||
-- ### Test Missions:
|
||||
--
|
||||
-- * SCH - Scheduler
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Scheduler
|
||||
@ -51,6 +46,153 @@
|
||||
-- @type SCHEDULER
|
||||
-- @field #number ScheduleID the ID of the scheduler.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- # SCHEDULER class, extends @{Base#BASE}
|
||||
--
|
||||
-- The SCHEDULER class creates schedule.
|
||||
--
|
||||
-- A SCHEDULER can manage **multiple** (repeating) schedules. Each planned or executing schedule has a unique **ScheduleID**.
|
||||
-- The ScheduleID is returned when the method @{#SCHEDULER.Schedule}() is called.
|
||||
-- It is recommended to store the ScheduleID in a variable, as it is used in the methods @{SCHEDULER.Start}() and @{SCHEDULER.Stop}(),
|
||||
-- which can start and stop specific repeating schedules respectively within a SCHEDULER object.
|
||||
--
|
||||
-- ## SCHEDULER constructor
|
||||
--
|
||||
-- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters:
|
||||
--
|
||||
-- The @{#SCHEDULER.New}() method returns 2 variables:
|
||||
--
|
||||
-- 1. The SCHEDULER object reference.
|
||||
-- 2. The first schedule planned in the SCHEDULER object.
|
||||
--
|
||||
-- To clarify the different appliances, lets have a look at the following examples:
|
||||
--
|
||||
-- ### Construct a SCHEDULER object without a persistent schedule.
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection.
|
||||
--
|
||||
-- SchedulerObject = SCHEDULER:New()
|
||||
-- SchedulerID = SchedulerObject:Schedule( nil, ScheduleFunction, {} )
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, but does not schedule anything.
|
||||
-- A separate schedule is created by using the SchedulerObject using the method :Schedule..., which returns a ScheduleID
|
||||
--
|
||||
-- ### Construct a SCHEDULER object without a volatile schedule, but volatile to the Object existence...
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection.
|
||||
--
|
||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||
-- SchedulerObject = SCHEDULER:New( ZoneObject )
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
-- ...
|
||||
-- ZoneObject = nil
|
||||
-- garbagecollect()
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, but does not schedule anything, and is bound to the existence of ZoneObject, which is a ZONE.
|
||||
-- A separate schedule is created by using the SchedulerObject using the method :Schedule()..., which returns a ScheduleID
|
||||
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
|
||||
-- As a result, the ScheduleObject will cancel any planned schedule.
|
||||
--
|
||||
-- ### Construct a SCHEDULER object with a persistent schedule.
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
|
||||
--
|
||||
-- SchedulerObject, SchedulerID = SCHEDULER:New( nil, ScheduleFunction, {} )
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, and does schedule the first schedule as part of the call.
|
||||
-- Note that 2 variables are returned here: SchedulerObject, ScheduleID...
|
||||
--
|
||||
-- ### Construct a SCHEDULER object without a schedule, but volatile to the Object existence...
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
|
||||
--
|
||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||
-- SchedulerObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
-- ...
|
||||
-- ZoneObject = nil
|
||||
-- garbagecollect()
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, and schedules a method call (ScheduleFunction),
|
||||
-- and is bound to the existence of ZoneObject, which is a ZONE object (ZoneObject).
|
||||
-- Both a ScheduleObject and a SchedulerID variable are returned.
|
||||
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
|
||||
-- As a result, the ScheduleObject will cancel the planned schedule.
|
||||
--
|
||||
-- ## SCHEDULER timer stopping and (re-)starting.
|
||||
--
|
||||
-- The SCHEDULER can be stopped and restarted with the following methods:
|
||||
--
|
||||
-- * @{#SCHEDULER.Start}(): (Re-)Start the schedules within the SCHEDULER object. If a CallID is provided to :Start(), only the schedule referenced by CallID will be (re-)started.
|
||||
-- * @{#SCHEDULER.Stop}(): Stop the schedules within the SCHEDULER object. If a CallID is provided to :Stop(), then only the schedule referenced by CallID will be stopped.
|
||||
--
|
||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||
-- SchedulerObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 10 )
|
||||
-- ...
|
||||
-- SchedulerObject:Stop( SchedulerID )
|
||||
-- ...
|
||||
-- SchedulerObject:Start( SchedulerID )
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, and does schedule the first schedule as part of the call.
|
||||
-- Note that 2 variables are returned here: SchedulerObject, ScheduleID...
|
||||
-- Later in the logic, the repeating schedule with SchedulerID is stopped.
|
||||
-- A bit later, the repeating schedule with SchedulerId is (re)-started.
|
||||
--
|
||||
-- ## Create a new schedule
|
||||
--
|
||||
-- With the method @{#SCHEDULER.Schedule}() a new time event can be scheduled.
|
||||
-- This method is used by the :New() constructor when a new schedule is planned.
|
||||
--
|
||||
-- Consider the following code fragment of the SCHEDULER object creation.
|
||||
--
|
||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||
-- SchedulerObject = SCHEDULER:New( ZoneObject )
|
||||
--
|
||||
-- Several parameters can be specified that influence the behaviour of a Schedule.
|
||||
--
|
||||
-- ### A single schedule, immediately executed
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milleseconds ...
|
||||
--
|
||||
-- ### A single schedule, planned over time
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10 )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds ...
|
||||
--
|
||||
-- ### A schedule with a repeating time interval, planned over time
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60 )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
||||
-- and repeating 60 every seconds ...
|
||||
--
|
||||
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5 )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
||||
-- and repeating 60 seconds, with a 50% time interval randomization ...
|
||||
-- So the repeating time interval will be randomized using the **0.5**,
|
||||
-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat,
|
||||
-- which is in this example between **30** and **90** seconds.
|
||||
--
|
||||
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization, and stop after a time interval
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5, 300 )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
||||
-- The schedule will repeat every 60 seconds.
|
||||
-- So the repeating time interval will be randomized using the **0.5**,
|
||||
-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat,
|
||||
-- which is in this example between **30** and **90** seconds.
|
||||
-- The schedule will stop after **300** seconds.
|
||||
--
|
||||
-- @field #SCHEDULER
|
||||
SCHEDULER = {
|
||||
ClassName = "SCHEDULER",
|
||||
Schedules = {},
|
||||
|
||||
@ -124,7 +124,7 @@ end
|
||||
-- @param Core.Base#BASE Object
|
||||
-- @return Core.Base#BASE The added BASE Object.
|
||||
function SET_BASE:Add( ObjectName, Object )
|
||||
self:F2( ObjectName )
|
||||
self:F( ObjectName )
|
||||
|
||||
local t = { _ = Object }
|
||||
|
||||
@ -1374,7 +1374,7 @@ function SET_UNIT:ForEachUnitCompletelyInZone( ZoneObject, IteratorFunction, ...
|
||||
--- @param Core.Zone#ZONE_BASE ZoneObject
|
||||
-- @param Wrapper.Unit#UNIT UnitObject
|
||||
function( ZoneObject, UnitObject )
|
||||
if UnitObject:IsCompletelyInZone( ZoneObject ) then
|
||||
if UnitObject:IsInZone( ZoneObject ) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
@ -2391,3 +2391,346 @@ function SET_AIRBASE:IsIncludeObject( MAirbase )
|
||||
self:T2( MAirbaseInclude )
|
||||
return MAirbaseInclude
|
||||
end
|
||||
|
||||
--- @type SET_CARGO
|
||||
-- @extends Core.Set#SET_BASE
|
||||
|
||||
--- # SET_CARGO class, extends @{Set#SET_BASE}
|
||||
--
|
||||
-- Mission designers can use the @{Set#SET_CARGO} class to build sets of cargos optionally belonging to certain:
|
||||
--
|
||||
-- * Coalitions
|
||||
-- * Types
|
||||
-- * Name or Prefix
|
||||
--
|
||||
-- ## SET_CARGO constructor
|
||||
--
|
||||
-- Create a new SET_CARGO object with the @{#SET_CARGO.New} method:
|
||||
--
|
||||
-- * @{#SET_CARGO.New}: Creates a new SET_CARGO object.
|
||||
--
|
||||
-- ## Add or Remove CARGOs from SET_CARGO
|
||||
--
|
||||
-- CARGOs can be added and removed using the @{Set#SET_CARGO.AddCargosByName} and @{Set#SET_CARGO.RemoveCargosByName} respectively.
|
||||
-- These methods take a single CARGO name or an array of CARGO names to be added or removed from SET_CARGO.
|
||||
--
|
||||
-- ## SET_CARGO filter criteria
|
||||
--
|
||||
-- You can set filter criteria to automatically maintain the SET_CARGO contents.
|
||||
-- Filter criteria are defined by:
|
||||
--
|
||||
-- * @{#SET_CARGO.FilterCoalitions}: Builds the SET_CARGO with the cargos belonging to the coalition(s).
|
||||
-- * @{#SET_CARGO.FilterPrefixes}: Builds the SET_CARGO with the cargos containing the prefix string(s).
|
||||
-- * @{#SET_CARGO.FilterTypes}: Builds the SET_CARGO with the cargos belonging to the cargo type(s).
|
||||
-- * @{#SET_CARGO.FilterCountries}: Builds the SET_CARGO with the cargos belonging to the country(ies).
|
||||
--
|
||||
-- Once the filter criteria have been set for the SET_CARGO, you can start filtering using:
|
||||
--
|
||||
-- * @{#SET_CARGO.FilterStart}: Starts the filtering of the cargos within the SET_CARGO.
|
||||
--
|
||||
-- ## SET_CARGO iterators
|
||||
--
|
||||
-- Once the filters have been defined and the SET_CARGO has been built, you can iterate the SET_CARGO with the available iterator methods.
|
||||
-- The iterator methods will walk the SET_CARGO set, and call for each cargo within the set a function that you provide.
|
||||
-- The following iterator methods are currently available within the SET_CARGO:
|
||||
--
|
||||
-- * @{#SET_CARGO.ForEachCargo}: Calls a function for each cargo it finds within the SET_CARGO.
|
||||
--
|
||||
-- @field #SET_CARGO SET_CARGO
|
||||
--
|
||||
SET_CARGO = {
|
||||
ClassName = "SET_CARGO",
|
||||
Cargos = {},
|
||||
Filter = {
|
||||
Coalitions = nil,
|
||||
Types = nil,
|
||||
Countries = nil,
|
||||
ClientPrefixes = nil,
|
||||
},
|
||||
FilterMeta = {
|
||||
Coalitions = {
|
||||
red = coalition.side.RED,
|
||||
blue = coalition.side.BLUE,
|
||||
neutral = coalition.side.NEUTRAL,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new SET_CARGO object, building a set of cargos belonging to a coalitions and categories.
|
||||
-- @param #SET_CARGO self
|
||||
-- @return #SET_CARGO self
|
||||
-- @usage
|
||||
-- -- Define a new SET_CARGO Object. The DatabaseSet will contain a reference to all Cargos.
|
||||
-- DatabaseSet = SET_CARGO:New()
|
||||
function SET_CARGO:New()
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CARGOS ) )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add CARGOs to SET_CARGO.
|
||||
-- @param Core.Set#SET_CARGO self
|
||||
-- @param #string AddCargoNames A single name or an array of CARGO names.
|
||||
-- @return self
|
||||
function SET_CARGO:AddCargosByName( AddCargoNames )
|
||||
|
||||
local AddCargoNamesArray = ( type( AddCargoNames ) == "table" ) and AddCargoNames or { AddCargoNames }
|
||||
|
||||
for AddCargoID, AddCargoName in pairs( AddCargoNamesArray ) do
|
||||
self:Add( AddCargoName, CARGO:FindByName( AddCargoName ) )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove CARGOs from SET_CARGO.
|
||||
-- @param Core.Set#SET_CARGO self
|
||||
-- @param Wrapper.Cargo#CARGO RemoveCargoNames A single name or an array of CARGO names.
|
||||
-- @return self
|
||||
function SET_CARGO:RemoveCargosByName( RemoveCargoNames )
|
||||
|
||||
local RemoveCargoNamesArray = ( type( RemoveCargoNames ) == "table" ) and RemoveCargoNames or { RemoveCargoNames }
|
||||
|
||||
for RemoveCargoID, RemoveCargoName in pairs( RemoveCargoNamesArray ) do
|
||||
self:Remove( RemoveCargoName.CargoName )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Finds a Cargo based on the Cargo Name.
|
||||
-- @param #SET_CARGO self
|
||||
-- @param #string CargoName
|
||||
-- @return Wrapper.Cargo#CARGO The found Cargo.
|
||||
function SET_CARGO:FindCargo( CargoName )
|
||||
|
||||
local CargoFound = self.Set[CargoName]
|
||||
return CargoFound
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Builds a set of cargos of coalitions.
|
||||
-- Possible current coalitions are red, blue and neutral.
|
||||
-- @param #SET_CARGO self
|
||||
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral".
|
||||
-- @return #SET_CARGO self
|
||||
function SET_CARGO:FilterCoalitions( Coalitions )
|
||||
if not self.Filter.Coalitions then
|
||||
self.Filter.Coalitions = {}
|
||||
end
|
||||
if type( Coalitions ) ~= "table" then
|
||||
Coalitions = { Coalitions }
|
||||
end
|
||||
for CoalitionID, Coalition in pairs( Coalitions ) do
|
||||
self.Filter.Coalitions[Coalition] = Coalition
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Builds a set of cargos of defined cargo types.
|
||||
-- Possible current types are those types known within DCS world.
|
||||
-- @param #SET_CARGO self
|
||||
-- @param #string Types Can take those type strings known within DCS world.
|
||||
-- @return #SET_CARGO self
|
||||
function SET_CARGO:FilterTypes( Types )
|
||||
if not self.Filter.Types then
|
||||
self.Filter.Types = {}
|
||||
end
|
||||
if type( Types ) ~= "table" then
|
||||
Types = { Types }
|
||||
end
|
||||
for TypeID, Type in pairs( Types ) do
|
||||
self.Filter.Types[Type] = Type
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Builds a set of cargos of defined countries.
|
||||
-- Possible current countries are those known within DCS world.
|
||||
-- @param #SET_CARGO self
|
||||
-- @param #string Countries Can take those country strings known within DCS world.
|
||||
-- @return #SET_CARGO self
|
||||
function SET_CARGO:FilterCountries( Countries )
|
||||
if not self.Filter.Countries then
|
||||
self.Filter.Countries = {}
|
||||
end
|
||||
if type( Countries ) ~= "table" then
|
||||
Countries = { Countries }
|
||||
end
|
||||
for CountryID, Country in pairs( Countries ) do
|
||||
self.Filter.Countries[Country] = Country
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Builds a set of cargos of defined cargo prefixes.
|
||||
-- All the cargos starting with the given prefixes will be included within the set.
|
||||
-- @param #SET_CARGO self
|
||||
-- @param #string Prefixes The prefix of which the cargo name starts with.
|
||||
-- @return #SET_CARGO self
|
||||
function SET_CARGO:FilterPrefixes( Prefixes )
|
||||
if not self.Filter.CargoPrefixes then
|
||||
self.Filter.CargoPrefixes = {}
|
||||
end
|
||||
if type( Prefixes ) ~= "table" then
|
||||
Prefixes = { Prefixes }
|
||||
end
|
||||
for PrefixID, Prefix in pairs( Prefixes ) do
|
||||
self.Filter.CargoPrefixes[Prefix] = Prefix
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Starts the filtering.
|
||||
-- @param #SET_CARGO self
|
||||
-- @return #SET_CARGO self
|
||||
function SET_CARGO:FilterStart()
|
||||
|
||||
if _DATABASE then
|
||||
self:_FilterStart()
|
||||
end
|
||||
|
||||
self:HandleEvent( EVENTS.NewCargo )
|
||||
self:HandleEvent( EVENTS.DeleteCargo )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Handles the Database to check on an event (birth) that the Object was added in the Database.
|
||||
-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event!
|
||||
-- @param #SET_CARGO self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
-- @return #string The name of the CARGO
|
||||
-- @return #table The CARGO
|
||||
function SET_CARGO:AddInDatabase( Event )
|
||||
self:F3( { Event } )
|
||||
|
||||
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
||||
end
|
||||
|
||||
--- Handles the Database to check on any event that Object exists in the Database.
|
||||
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
|
||||
-- @param #SET_CARGO self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
-- @return #string The name of the CARGO
|
||||
-- @return #table The CARGO
|
||||
function SET_CARGO:FindInDatabase( Event )
|
||||
self:F3( { Event } )
|
||||
|
||||
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
||||
end
|
||||
|
||||
--- Iterate the SET_CARGO and call an interator function for each CARGO, providing the CARGO and optional parameters.
|
||||
-- @param #SET_CARGO self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive CARGO in the SET_CARGO. The function needs to accept a CARGO parameter.
|
||||
-- @return #SET_CARGO self
|
||||
function SET_CARGO:ForEachCargo( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
self:ForEach( IteratorFunction, arg, self.Set )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Iterate the SET_CARGO while identifying the nearest @{Cargo#CARGO} from a @{Point#POINT_VEC2}.
|
||||
-- @param #SET_CARGO self
|
||||
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Point#POINT_VEC2} object from where to evaluate the closest @{Cargo#CARGO}.
|
||||
-- @return Wrapper.Cargo#CARGO The closest @{Cargo#CARGO}.
|
||||
function SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 )
|
||||
self:F2( PointVec2 )
|
||||
|
||||
local NearestCargo = self:FindNearestObjectFromPointVec2( PointVec2 )
|
||||
return NearestCargo
|
||||
end
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- @param #SET_CARGO self
|
||||
-- @param AI.AI_Cargo#AI_CARGO MCargo
|
||||
-- @return #SET_CARGO self
|
||||
function SET_CARGO:IsIncludeObject( MCargo )
|
||||
self:F2( MCargo )
|
||||
|
||||
local MCargoInclude = true
|
||||
|
||||
if MCargo then
|
||||
local MCargoName = MCargo:GetName()
|
||||
|
||||
if self.Filter.Coalitions then
|
||||
local MCargoCoalition = false
|
||||
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
|
||||
local CargoCoalitionID = MCargo:GetCoalition()
|
||||
self:T3( { "Coalition:", CargoCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
|
||||
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == CargoCoalitionID then
|
||||
MCargoCoalition = true
|
||||
end
|
||||
end
|
||||
self:T( { "Evaluated Coalition", MCargoCoalition } )
|
||||
MCargoInclude = MCargoInclude and MCargoCoalition
|
||||
end
|
||||
|
||||
if self.Filter.Types then
|
||||
local MCargoType = false
|
||||
for TypeID, TypeName in pairs( self.Filter.Types ) do
|
||||
self:T3( { "Type:", MCargo:GetType(), TypeName } )
|
||||
if TypeName == MCargo:GetType() then
|
||||
MCargoType = true
|
||||
end
|
||||
end
|
||||
self:T( { "Evaluated Type", MCargoType } )
|
||||
MCargoInclude = MCargoInclude and MCargoType
|
||||
end
|
||||
|
||||
if self.Filter.CargoPrefixes then
|
||||
local MCargoPrefix = false
|
||||
for CargoPrefixId, CargoPrefix in pairs( self.Filter.CargoPrefixes ) do
|
||||
self:T3( { "Prefix:", string.find( MCargo.Name, CargoPrefix, 1 ), CargoPrefix } )
|
||||
if string.find( MCargo.Name, CargoPrefix, 1 ) then
|
||||
MCargoPrefix = true
|
||||
end
|
||||
end
|
||||
self:T( { "Evaluated Prefix", MCargoPrefix } )
|
||||
MCargoInclude = MCargoInclude and MCargoPrefix
|
||||
end
|
||||
end
|
||||
|
||||
self:T2( MCargoInclude )
|
||||
return MCargoInclude
|
||||
end
|
||||
|
||||
--- Handles the OnEventNewCargo event for the Set.
|
||||
-- @param #SET_CARGO self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function SET_CARGO:OnEventNewCargo( EventData )
|
||||
|
||||
if EventData.Cargo then
|
||||
if EventData.Cargo and self:IsIncludeObject( EventData.Cargo ) then
|
||||
self:Add( EventData.Cargo.Name , EventData.Cargo )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnDead or OnCrash event for alive units set.
|
||||
-- @param #SET_CARGO self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function SET_CARGO:OnEventDeleteCargo( EventData )
|
||||
self:F3( { EventData } )
|
||||
|
||||
if EventData.Cargo then
|
||||
local Cargo = _DATABASE:FindCargo( EventData.Cargo.Name )
|
||||
if Cargo and Cargo.Name then
|
||||
self:Remove( Cargo.Name )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
177
Moose Development/Moose/Core/SpawnStatic.lua
Normal file
177
Moose Development/Moose/Core/SpawnStatic.lua
Normal file
@ -0,0 +1,177 @@
|
||||
--- **Core** -- Spawn dynamically new STATICs in your missions.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- SPAWNSTATIC spawns static structures in your missions dynamically. See below the SPAWNSTATIC class documentation.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- ### [SPAWNSTATIC Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPS - Spawning Statics)
|
||||
--
|
||||
-- ### [SPAWNSTATIC Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPS%20-%20Spawning%20Statics)
|
||||
--
|
||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ### [SPAWNSTATIC YouTube Channel]()
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # **API CHANGE HISTORY**
|
||||
--
|
||||
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
|
||||
--
|
||||
-- * **Added** parts are expressed in bold type face.
|
||||
-- * _Removed_ parts are expressed in italic type face.
|
||||
--
|
||||
-- Hereby the change log:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Design & Programming
|
||||
--
|
||||
-- @module SpawnStatic
|
||||
|
||||
|
||||
|
||||
--- @type SPAWNSTATIC
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- # SPAWNSTATIC class, extends @{Base#BASE}
|
||||
--
|
||||
-- The SPAWNSTATIC class allows to spawn dynamically new @{Static}s.
|
||||
-- Through creating a copy of an existing static object template as defined in the Mission Editor (ME),
|
||||
-- SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), and "copy"
|
||||
-- these properties to create a new static object and place it at the desired coordinate.
|
||||
--
|
||||
-- New spawned @{Static}s get **the same name** as the name of the template Static,
|
||||
-- or gets the given name when a new name is provided at the Spawn method.
|
||||
-- By default, spawned @{Static}s will follow a naming convention at run-time:
|
||||
--
|
||||
-- * Spawned @{Static}s will have the name _StaticName_#_nnn_, where _StaticName_ is the name of the **Template Static**,
|
||||
-- and _nnn_ is a **counter from 0 to 99999**.
|
||||
--
|
||||
--
|
||||
-- ## SPAWNSTATIC construction methods
|
||||
--
|
||||
-- Create a new SPAWNSTATIC object with the @{#SPAWNSTATIC.NewFromStatic}():
|
||||
--
|
||||
-- * @{#SPAWNSTATIC.NewFromStatic}(): Creates a new SPAWNSTATIC object given a name that is used as the base of the naming of each spawned Static.
|
||||
--
|
||||
-- ## **Spawn** methods
|
||||
--
|
||||
-- Groups can be spawned at different times and methods:
|
||||
--
|
||||
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(): Spawn a new group from a POINT_VEC2 coordinate.
|
||||
-- (The group will be spawned at land height ).
|
||||
-- * @{#SPAWNSTATIC.SpawnFromZone}(): Spawn a new group in a @{Zone}.
|
||||
--
|
||||
-- @field #SPAWNSTATIC SPAWNSTATIC
|
||||
--
|
||||
SPAWNSTATIC = {
|
||||
ClassName = "SPAWNSTATIC",
|
||||
}
|
||||
|
||||
|
||||
--- @type SPAWNSTATIC.SpawnZoneTable
|
||||
-- @list <Core.Zone#ZONE_BASE> SpawnZone
|
||||
|
||||
|
||||
--- Creates the main object to spawn a @{Static} defined in the ME.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, CountryID )
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
|
||||
self:F( { SpawnTemplatePrefix } )
|
||||
|
||||
local TemplateStatic = StaticObject.getByName( SpawnTemplatePrefix )
|
||||
if TemplateStatic then
|
||||
self.SpawnTemplatePrefix = SpawnTemplatePrefix
|
||||
self.CountryID = CountryID
|
||||
self.SpawnIndex = 0
|
||||
else
|
||||
error( "SPAWNSTATIC:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
|
||||
end
|
||||
|
||||
self:SetEventPriority( 5 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates the main object to spawn a @{Static} based on a type name.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #string SpawnTypeName is the name of the type.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory, CountryID )
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
|
||||
self:F( { SpawnTypeName } )
|
||||
|
||||
self.SpawnTypeName = SpawnTypeName
|
||||
self.CountryID = CountryID
|
||||
self.SpawnIndex = 0
|
||||
|
||||
self:SetEventPriority( 5 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new @{Static} from a POINT_VEC2.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static.
|
||||
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
|
||||
-- @param #string (optional) The name of the new static.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName )
|
||||
self:F( { PointVec2, Heading, NewName } )
|
||||
|
||||
local CountryName = _DATABASE.COUNTRY_NAME[self.CountryID]
|
||||
|
||||
local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix )
|
||||
|
||||
StaticTemplate.x = PointVec2:GetLat()
|
||||
StaticTemplate.y = PointVec2:GetLon()
|
||||
|
||||
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
|
||||
StaticTemplate.heading = ( Heading / 180 ) * math.pi
|
||||
|
||||
StaticTemplate.CountryID = nil
|
||||
StaticTemplate.CoalitionID = nil
|
||||
StaticTemplate.CategoryID = nil
|
||||
|
||||
local Static = coalition.addStaticObject( self.CountryID, StaticTemplate )
|
||||
|
||||
self.SpawnIndex = self.SpawnIndex + 1
|
||||
|
||||
return Static
|
||||
end
|
||||
|
||||
--- Creates a new @{Static} from a @{Zone}.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param Core.Zone#ZONE_BASE Zone The Zone where to spawn the static.
|
||||
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
|
||||
-- @param #string (optional) The name of the new static.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:SpawnFromZone( Zone, Heading, NewName )
|
||||
self:F( { Zone, Heading, NewName } )
|
||||
|
||||
local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName )
|
||||
|
||||
return Static
|
||||
end
|
||||
|
||||
@ -66,48 +66,53 @@
|
||||
-- @module Zone
|
||||
|
||||
|
||||
--- The ZONE_BASE class
|
||||
-- @type ZONE_BASE
|
||||
--- @type ZONE_BASE
|
||||
-- @field #string ZoneName Name of the zone.
|
||||
-- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- # 1) ZONE_BASE class, extends @{Base#BASE}
|
||||
--- # ZONE_BASE class, extends @{Base#BASE}
|
||||
--
|
||||
-- This class is an abstract BASE class for derived classes, and is not meant to be instantiated.
|
||||
--
|
||||
-- ## 1.1) Each zone has a name:
|
||||
-- ## Each zone has a name:
|
||||
--
|
||||
-- * @{#ZONE_BASE.GetName}(): Returns the name of the zone.
|
||||
--
|
||||
-- ## 1.2) Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}:
|
||||
-- ## Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}:
|
||||
--
|
||||
-- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a Vec2 is within the zone.
|
||||
-- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a Vec3 is within the zone.
|
||||
-- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a 2D vector is within the zone.
|
||||
-- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a 3D vector is within the zone.
|
||||
-- * @{#ZONE_BASE.IsPointVec2InZone}(): Returns if a 2D point vector is within the zone.
|
||||
-- * @{#ZONE_BASE.IsPointVec3InZone}(): Returns if a 3D point vector is within the zone.
|
||||
--
|
||||
-- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones:
|
||||
-- ## A zone has a probability factor that can be set to randomize a selection between zones:
|
||||
--
|
||||
-- * @{#ZONE_BASE.SetRandomizeProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% )
|
||||
-- * @{#ZONE_BASE.GetRandomizeProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% )
|
||||
-- * @{#ZONE_BASE.SetZoneProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% )
|
||||
-- * @{#ZONE_BASE.GetZoneProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% )
|
||||
-- * @{#ZONE_BASE.GetZoneMaybe}(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate.
|
||||
--
|
||||
-- ## 1.4) A zone manages Vectors:
|
||||
-- ## A zone manages vectors:
|
||||
--
|
||||
-- * @{#ZONE_BASE.GetVec2}(): Returns the @{DCSTypes#Vec2} coordinate of the zone.
|
||||
-- * @{#ZONE_BASE.GetRandomVec2}(): Define a random @{DCSTypes#Vec2} within the zone.
|
||||
-- * @{#ZONE_BASE.GetVec2}(): Returns the 2D vector coordinate of the zone.
|
||||
-- * @{#ZONE_BASE.GetVec3}(): Returns the 3D vector coordinate of the zone.
|
||||
-- * @{#ZONE_BASE.GetPointVec2}(): Returns the 2D point vector coordinate of the zone.
|
||||
-- * @{#ZONE_BASE.GetPointVec3}(): Returns the 3D point vector coordinate of the zone.
|
||||
-- * @{#ZONE_BASE.GetRandomVec2}(): Define a random 2D vector within the zone.
|
||||
-- * @{#ZONE_BASE.GetRandomPointVec2}(): Define a random 2D point vector within the zone.
|
||||
-- * @{#ZONE_BASE.GetRandomPointVec3}(): Define a random 3D point vector within the zone.
|
||||
--
|
||||
-- ## 1.5) A zone has a bounding square:
|
||||
-- ## A zone has a bounding square:
|
||||
--
|
||||
-- * @{#ZONE_BASE.GetBoundingSquare}(): Get the outer most bounding square of the zone.
|
||||
--
|
||||
-- ## 1.6) A zone can be marked:
|
||||
-- ## A zone can be marked:
|
||||
--
|
||||
-- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color.
|
||||
-- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color.
|
||||
--
|
||||
-- ===
|
||||
-- @field #ZONE_BASE ZONE_BASE
|
||||
-- @field #ZONE_BASE
|
||||
ZONE_BASE = {
|
||||
ClassName = "ZONE_BASE",
|
||||
ZoneName = "",
|
||||
@ -144,20 +149,21 @@ function ZONE_BASE:GetName()
|
||||
|
||||
return self.ZoneName
|
||||
end
|
||||
--- Returns if a location is within the zone.
|
||||
|
||||
--- Returns if a Vec2 is within the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param Dcs.DCSTypes#Vec2 Vec2 The location to test.
|
||||
-- @return #boolean true if the location is within the zone.
|
||||
-- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 to test.
|
||||
-- @return #boolean true if the Vec2 is within the zone.
|
||||
function ZONE_BASE:IsVec2InZone( Vec2 )
|
||||
self:F2( Vec2 )
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--- Returns if a point is within the zone.
|
||||
--- Returns if a Vec3 is within the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param Dcs.DCSTypes#Vec3 Vec3 The point to test.
|
||||
-- @return #boolean true if the point is within the zone.
|
||||
-- @return #boolean true if the Vec3 is within the zone.
|
||||
function ZONE_BASE:IsVec3InZone( Vec3 )
|
||||
self:F2( Vec3 )
|
||||
|
||||
@ -166,6 +172,31 @@ function ZONE_BASE:IsVec3InZone( Vec3 )
|
||||
return InZone
|
||||
end
|
||||
|
||||
--- Returns if a PointVec2 is within the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to test.
|
||||
-- @return #boolean true if the PointVec2 is within the zone.
|
||||
function ZONE_BASE:IsPointVec2InZone( PointVec2 )
|
||||
self:F2( PointVec2 )
|
||||
|
||||
local InZone = self:IsVec2InZone( PointVec2:GetVec2() )
|
||||
|
||||
return InZone
|
||||
end
|
||||
|
||||
--- Returns if a PointVec3 is within the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 to test.
|
||||
-- @return #boolean true if the PointVec3 is within the zone.
|
||||
function ZONE_BASE:IsPointVec3InZone( PointVec3 )
|
||||
self:F2( PointVec3 )
|
||||
|
||||
local InZone = self:IsPointVec2InZone( PointVec3 )
|
||||
|
||||
return InZone
|
||||
end
|
||||
|
||||
|
||||
--- Returns the @{DCSTypes#Vec2} coordinate of the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @return #nil.
|
||||
@ -310,29 +341,29 @@ end
|
||||
-- @type ZONE_RADIUS
|
||||
-- @field Dcs.DCSTypes#Vec2 Vec2 The current location of the zone.
|
||||
-- @field Dcs.DCSTypes#Distance Radius The radius of the zone.
|
||||
-- @extends Core.Zone#ZONE_BASE
|
||||
-- @extends #ZONE_BASE
|
||||
|
||||
--- # 2) @{Zone#ZONE_RADIUS} class, extends @{Zone#ZONE_BASE}
|
||||
--- # ZONE_RADIUS class, extends @{Zone#ZONE_BASE}
|
||||
--
|
||||
-- The ZONE_RADIUS class defined by a zone name, a location and a radius.
|
||||
-- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties.
|
||||
--
|
||||
-- ## 2.1) @{Zone#ZONE_RADIUS} constructor
|
||||
-- ## ZONE_RADIUS constructor
|
||||
--
|
||||
-- * @{#ZONE_RADIUS.New}(): Constructor.
|
||||
--
|
||||
-- ## 2.2) Manage the radius of the zone
|
||||
-- ## Manage the radius of the zone
|
||||
--
|
||||
-- * @{#ZONE_RADIUS.SetRadius}(): Sets the radius of the zone.
|
||||
-- * @{#ZONE_RADIUS.GetRadius}(): Returns the radius of the zone.
|
||||
--
|
||||
-- ## 2.3) Manage the location of the zone
|
||||
-- ## Manage the location of the zone
|
||||
--
|
||||
-- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCSTypes#Vec2} of the zone.
|
||||
-- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCSTypes#Vec2} of the zone.
|
||||
-- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCSTypes#Vec3} of the zone, taking an additional height parameter.
|
||||
--
|
||||
-- ## 2.4) Zone point randomization
|
||||
-- ## Zone point randomization
|
||||
--
|
||||
-- Various functions exist to find random points within the zone.
|
||||
--
|
||||
@ -340,10 +371,7 @@ end
|
||||
-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Point#POINT_VEC2} object representing a random 2D point in the zone.
|
||||
-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #ZONE_RADIUS ZONE_RADIUS
|
||||
--
|
||||
-- @field #ZONE_RADIUS
|
||||
ZONE_RADIUS = {
|
||||
ClassName="ZONE_RADIUS",
|
||||
}
|
||||
@ -615,19 +643,16 @@ end
|
||||
|
||||
|
||||
|
||||
-- @type ZONE
|
||||
-- @extends Core.Zone#ZONE_RADIUS
|
||||
--- @type ZONE
|
||||
-- @extends #ZONE_RADIUS
|
||||
|
||||
|
||||
--- # 3) ZONE class, extends @{Zone#ZONE_RADIUS}
|
||||
--- # ZONE class, extends @{Zone#ZONE_RADIUS}
|
||||
--
|
||||
-- The ZONE class, defined by the zone name as defined within the Mission Editor.
|
||||
-- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #ZONE ZONE
|
||||
--
|
||||
-- @field #ZONE
|
||||
ZONE = {
|
||||
ClassName="ZONE",
|
||||
}
|
||||
@ -655,20 +680,16 @@ function ZONE:New( ZoneName )
|
||||
end
|
||||
|
||||
|
||||
--- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius.
|
||||
-- @type ZONE_UNIT
|
||||
--- @type ZONE_UNIT
|
||||
-- @field Wrapper.Unit#UNIT ZoneUNIT
|
||||
-- @extends Core.Zone#ZONE_RADIUS
|
||||
|
||||
--- # 4) #ZONE_UNIT class, extends @{Zone#ZONE_RADIUS}
|
||||
--- # ZONE_UNIT class, extends @{Zone#ZONE_RADIUS}
|
||||
--
|
||||
-- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius.
|
||||
-- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #ZONE_UNIT ZONE_UNIT
|
||||
--
|
||||
-- @field #ZONE_UNIT
|
||||
ZONE_UNIT = {
|
||||
ClassName="ZONE_UNIT",
|
||||
}
|
||||
@ -750,19 +771,15 @@ function ZONE_UNIT:GetVec3( Height )
|
||||
end
|
||||
|
||||
--- @type ZONE_GROUP
|
||||
-- @field Wrapper.Group#GROUP ZoneGROUP
|
||||
-- @extends Core.Zone#ZONE_RADIUS
|
||||
-- @extends #ZONE_RADIUS
|
||||
|
||||
|
||||
--- # 5) #ZONE_GROUP class, extends @{Zone#ZONE_RADIUS}
|
||||
--- # ZONE_GROUP class, extends @{Zone#ZONE_RADIUS}
|
||||
--
|
||||
-- The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. The current leader of the group defines the center of the zone.
|
||||
-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #ZONE_GROUP ZONE_GROUP
|
||||
--
|
||||
-- @field #ZONE_GROUP
|
||||
ZONE_GROUP = {
|
||||
ClassName="ZONE_GROUP",
|
||||
}
|
||||
@ -777,7 +794,7 @@ function ZONE_GROUP:New( ZoneName, ZoneGROUP, Radius )
|
||||
local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneGROUP:GetVec2(), Radius ) )
|
||||
self:F( { ZoneName, ZoneGROUP:GetVec2(), Radius } )
|
||||
|
||||
self.ZoneGROUP = ZoneGROUP
|
||||
self._.ZoneGROUP = ZoneGROUP
|
||||
|
||||
return self
|
||||
end
|
||||
@ -789,7 +806,7 @@ end
|
||||
function ZONE_GROUP:GetVec2()
|
||||
self:F( self.ZoneName )
|
||||
|
||||
local ZoneVec2 = self.ZoneGROUP:GetVec2()
|
||||
local ZoneVec2 = self._.ZoneGROUP:GetVec2()
|
||||
|
||||
self:T( { ZoneVec2 } )
|
||||
|
||||
@ -803,7 +820,7 @@ function ZONE_GROUP:GetRandomVec2()
|
||||
self:F( self.ZoneName )
|
||||
|
||||
local Point = {}
|
||||
local Vec2 = self.ZoneGROUP:GetVec2()
|
||||
local Vec2 = self._.ZoneGROUP:GetVec2()
|
||||
|
||||
local angle = math.random() * math.pi*2;
|
||||
Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius();
|
||||
@ -817,17 +834,17 @@ end
|
||||
|
||||
|
||||
--- @type ZONE_POLYGON_BASE
|
||||
-- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCSTypes#Vec2}.
|
||||
-- @extends Core.Zone#ZONE_BASE
|
||||
-- --@field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCSTypes#Vec2}.
|
||||
-- @extends #ZONE_BASE
|
||||
|
||||
|
||||
--- # 6) ZONE_POLYGON_BASE class, extends @{Zone#ZONE_BASE}
|
||||
--- # ZONE_POLYGON_BASE class, extends @{Zone#ZONE_BASE}
|
||||
--
|
||||
-- The ZONE_POLYGON_BASE class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon.
|
||||
-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties.
|
||||
-- This class is an abstract BASE class for derived classes, and is not meant to be instantiated.
|
||||
--
|
||||
-- ## 6.1) Zone point randomization
|
||||
-- ## Zone point randomization
|
||||
--
|
||||
-- Various functions exist to find random points within the zone.
|
||||
--
|
||||
@ -835,10 +852,7 @@ end
|
||||
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Point#POINT_VEC2} object representing a random 2D point within the zone.
|
||||
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #ZONE_POLYGON_BASE ZONE_POLYGON_BASE
|
||||
--
|
||||
-- @field #ZONE_POLYGON_BASE
|
||||
ZONE_POLYGON_BASE = {
|
||||
ClassName="ZONE_POLYGON_BASE",
|
||||
}
|
||||
@ -859,24 +873,35 @@ function ZONE_POLYGON_BASE:New( ZoneName, PointsArray )
|
||||
|
||||
local i = 0
|
||||
|
||||
self.Polygon = {}
|
||||
self._.Polygon = {}
|
||||
|
||||
for i = 1, #PointsArray do
|
||||
self.Polygon[i] = {}
|
||||
self.Polygon[i].x = PointsArray[i].x
|
||||
self.Polygon[i].y = PointsArray[i].y
|
||||
self._.Polygon[i] = {}
|
||||
self._.Polygon[i].x = PointsArray[i].x
|
||||
self._.Polygon[i].y = PointsArray[i].y
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Returns the center location of the polygon.
|
||||
-- @param #ZONE_GROUP self
|
||||
-- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Group} location.
|
||||
function ZONE_POLYGON_BASE:GetVec2()
|
||||
self:F( self.ZoneName )
|
||||
|
||||
local Bounds = self:GetBoundingSquare()
|
||||
|
||||
return { x = ( Bounds.x2 + Bounds.x1 ) / 2, y = ( Bounds.y2 + Bounds.y1 ) / 2 }
|
||||
end
|
||||
|
||||
--- Flush polygon coordinates as a table in DCS.log.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @return #ZONE_POLYGON_BASE self
|
||||
function ZONE_POLYGON_BASE:Flush()
|
||||
self:F2()
|
||||
|
||||
self:E( { Polygon = self.ZoneName, Coordinates = self.Polygon } )
|
||||
self:E( { Polygon = self.ZoneName, Coordinates = self._.Polygon } )
|
||||
|
||||
return self
|
||||
end
|
||||
@ -892,17 +917,17 @@ function ZONE_POLYGON_BASE:BoundZone( UnBound )
|
||||
local Segments = 10
|
||||
|
||||
i = 1
|
||||
j = #self.Polygon
|
||||
j = #self._.Polygon
|
||||
|
||||
while i <= #self.Polygon do
|
||||
self:T( { i, j, self.Polygon[i], self.Polygon[j] } )
|
||||
while i <= #self._.Polygon do
|
||||
self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } )
|
||||
|
||||
local DeltaX = self.Polygon[j].x - self.Polygon[i].x
|
||||
local DeltaY = self.Polygon[j].y - self.Polygon[i].y
|
||||
local DeltaX = self._.Polygon[j].x - self._.Polygon[i].x
|
||||
local DeltaY = self._.Polygon[j].y - self._.Polygon[i].y
|
||||
|
||||
for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line.
|
||||
local PointX = self.Polygon[i].x + ( Segment * DeltaX / Segments )
|
||||
local PointY = self.Polygon[i].y + ( Segment * DeltaY / Segments )
|
||||
local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments )
|
||||
local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments )
|
||||
local Tire = {
|
||||
["country"] = "USA",
|
||||
["category"] = "Fortifications",
|
||||
@ -942,17 +967,17 @@ function ZONE_POLYGON_BASE:SmokeZone( SmokeColor )
|
||||
local Segments = 10
|
||||
|
||||
i = 1
|
||||
j = #self.Polygon
|
||||
j = #self._.Polygon
|
||||
|
||||
while i <= #self.Polygon do
|
||||
self:T( { i, j, self.Polygon[i], self.Polygon[j] } )
|
||||
while i <= #self._.Polygon do
|
||||
self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } )
|
||||
|
||||
local DeltaX = self.Polygon[j].x - self.Polygon[i].x
|
||||
local DeltaY = self.Polygon[j].y - self.Polygon[i].y
|
||||
local DeltaX = self._.Polygon[j].x - self._.Polygon[i].x
|
||||
local DeltaY = self._.Polygon[j].y - self._.Polygon[i].y
|
||||
|
||||
for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line.
|
||||
local PointX = self.Polygon[i].x + ( Segment * DeltaX / Segments )
|
||||
local PointY = self.Polygon[i].y + ( Segment * DeltaY / Segments )
|
||||
local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments )
|
||||
local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments )
|
||||
POINT_VEC2:New( PointX, PointY ):Smoke( SmokeColor )
|
||||
end
|
||||
j = i
|
||||
@ -978,12 +1003,12 @@ function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 )
|
||||
local InPolygon = false
|
||||
|
||||
Next = 1
|
||||
Prev = #self.Polygon
|
||||
Prev = #self._.Polygon
|
||||
|
||||
while Next <= #self.Polygon do
|
||||
self:T( { Next, Prev, self.Polygon[Next], self.Polygon[Prev] } )
|
||||
if ( ( ( self.Polygon[Next].y > Vec2.y ) ~= ( self.Polygon[Prev].y > Vec2.y ) ) and
|
||||
( Vec2.x < ( self.Polygon[Prev].x - self.Polygon[Next].x ) * ( Vec2.y - self.Polygon[Next].y ) / ( self.Polygon[Prev].y - self.Polygon[Next].y ) + self.Polygon[Next].x )
|
||||
while Next <= #self._.Polygon do
|
||||
self:T( { Next, Prev, self._.Polygon[Next], self._.Polygon[Prev] } )
|
||||
if ( ( ( self._.Polygon[Next].y > Vec2.y ) ~= ( self._.Polygon[Prev].y > Vec2.y ) ) and
|
||||
( Vec2.x < ( self._.Polygon[Prev].x - self._.Polygon[Next].x ) * ( Vec2.y - self._.Polygon[Next].y ) / ( self._.Polygon[Prev].y - self._.Polygon[Next].y ) + self._.Polygon[Next].x )
|
||||
) then
|
||||
InPolygon = not InPolygon
|
||||
end
|
||||
@ -1054,17 +1079,17 @@ end
|
||||
-- @return #ZONE_POLYGON_BASE.BoundingSquare The bounding square.
|
||||
function ZONE_POLYGON_BASE:GetBoundingSquare()
|
||||
|
||||
local x1 = self.Polygon[1].x
|
||||
local y1 = self.Polygon[1].y
|
||||
local x2 = self.Polygon[1].x
|
||||
local y2 = self.Polygon[1].y
|
||||
local x1 = self._.Polygon[1].x
|
||||
local y1 = self._.Polygon[1].y
|
||||
local x2 = self._.Polygon[1].x
|
||||
local y2 = self._.Polygon[1].y
|
||||
|
||||
for i = 2, #self.Polygon do
|
||||
self:T2( { self.Polygon[i], x1, y1, x2, y2 } )
|
||||
x1 = ( x1 > self.Polygon[i].x ) and self.Polygon[i].x or x1
|
||||
x2 = ( x2 < self.Polygon[i].x ) and self.Polygon[i].x or x2
|
||||
y1 = ( y1 > self.Polygon[i].y ) and self.Polygon[i].y or y1
|
||||
y2 = ( y2 < self.Polygon[i].y ) and self.Polygon[i].y or y2
|
||||
for i = 2, #self._.Polygon do
|
||||
self:T2( { self._.Polygon[i], x1, y1, x2, y2 } )
|
||||
x1 = ( x1 > self._.Polygon[i].x ) and self._.Polygon[i].x or x1
|
||||
x2 = ( x2 < self._.Polygon[i].x ) and self._.Polygon[i].x or x2
|
||||
y1 = ( y1 > self._.Polygon[i].y ) and self._.Polygon[i].y or y1
|
||||
y2 = ( y2 < self._.Polygon[i].y ) and self._.Polygon[i].y or y2
|
||||
|
||||
end
|
||||
|
||||
@ -1073,18 +1098,15 @@ end
|
||||
|
||||
|
||||
--- @type ZONE_POLYGON
|
||||
-- @extends Core.Zone#ZONE_POLYGON_BASE
|
||||
-- @extends #ZONE_POLYGON_BASE
|
||||
|
||||
|
||||
--- # 7) ZONE_POLYGON class, extends @{Zone#ZONE_POLYGON_BASE}
|
||||
--- # ZONE_POLYGON class, extends @{Zone#ZONE_POLYGON_BASE}
|
||||
--
|
||||
-- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon.
|
||||
-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #ZONE_POLYGON ZONE_POLYGON
|
||||
--
|
||||
-- @field #ZONE_POLYGON
|
||||
ZONE_POLYGON = {
|
||||
ClassName="ZONE_POLYGON",
|
||||
}
|
||||
@ -1100,7 +1122,7 @@ function ZONE_POLYGON:New( ZoneName, ZoneGroup )
|
||||
local GroupPoints = ZoneGroup:GetTaskRoute()
|
||||
|
||||
local self = BASE:Inherit( self, ZONE_POLYGON_BASE:New( ZoneName, GroupPoints ) )
|
||||
self:F( { ZoneName, ZoneGroup, self.Polygon } )
|
||||
self:F( { ZoneName, ZoneGroup, self._.Polygon } )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@ -7,8 +7,25 @@
|
||||
-- DETECTION classes facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units).
|
||||
-- DETECTION uses the in-built detection capabilities of DCS World, but adds new functionalities.
|
||||
--
|
||||
-- Please watch this [youtube video](https://youtu.be/C7p81dUwP-E) that explains the detection concepts.
|
||||
-- Find the DETECTION classes documentation further in this document in the globals section.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- ### [DETECTION Demo Missions and Source Code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/DET%20-%20Detection)
|
||||
--
|
||||
-- ### [DETECTION Demo Missions, only for Beta Testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/DET%20-%20Detection)
|
||||
--
|
||||
-- ### [ALL Demo Missions pack of the Latest Release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ### [DETECTION YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl3Cf5jpI6BS0sBOVWK__tji)
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
@ -23,16 +40,24 @@
|
||||
|
||||
do -- DETECTION_BASE
|
||||
|
||||
--- # 1) DETECTION_BASE class, extends @{Fsm#FSM}
|
||||
--- @type DETECTION_BASE
|
||||
-- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role.
|
||||
-- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected.
|
||||
-- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects.
|
||||
-- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified.
|
||||
-- @field #number DetectionRun
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- DETECTION_BASE class, extends @{Fsm#FSM}
|
||||
--
|
||||
-- The DETECTION_BASE class defines the core functions to administer detected objects.
|
||||
-- The DETECTION_BASE class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s).
|
||||
--
|
||||
-- ## 1.1) DETECTION_BASE constructor
|
||||
-- ## DETECTION_BASE constructor
|
||||
--
|
||||
-- Construct a new DETECTION_BASE instance using the @{#DETECTION_BASE.New}() method.
|
||||
--
|
||||
-- ## 1.2) DETECTION_BASE initialization
|
||||
-- ## Initialization
|
||||
--
|
||||
-- By default, detection will return detected objects with all the detection sensors available.
|
||||
-- However, you can ask how the objects were found with specific detection methods.
|
||||
@ -48,7 +73,29 @@ do -- DETECTION_BASE
|
||||
-- * @{#DETECTION_BASE.InitDetectRWR}(): Detected using RWR.
|
||||
-- * @{#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK.
|
||||
--
|
||||
-- ## 1.3) DETECTION_BASE derived classes group the detected units into a DetectedItems[] list
|
||||
-- ## **Filter** detected units based on **category of the unit**
|
||||
--
|
||||
-- Filter the detected units based on Unit.Category using the method @{#DETECTION_BASE.FilterCategories}().
|
||||
-- The different values of Unit.Category can be:
|
||||
--
|
||||
-- * Unit.Category.AIRPLANE
|
||||
-- * Unit.Category.GROUND_UNIT
|
||||
-- * Unit.Category.HELICOPTER
|
||||
-- * Unit.Category.SHIP
|
||||
-- * Unit.Category.STRUCTURE
|
||||
--
|
||||
-- Multiple Unit.Category entries can be given as a table and then these will be evaluated as an OR expression.
|
||||
--
|
||||
-- Example to filter a single category (Unit.Category.AIRPLANE).
|
||||
--
|
||||
-- DetectionObject:FilterCategories( Unit.Category.AIRPLANE )
|
||||
--
|
||||
-- Example to filter multiple categories (Unit.Category.AIRPLANE, Unit.Category.HELICOPTER). Note the {}.
|
||||
--
|
||||
-- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
|
||||
--
|
||||
--
|
||||
-- ## **DETECTION_ derived classes** group the detected units into a **DetectedItems[]** list
|
||||
--
|
||||
-- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later
|
||||
-- of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains
|
||||
@ -67,7 +114,7 @@ do -- DETECTION_BASE
|
||||
-- * A DetectedSet from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSet}( DetectedItemIndex ).
|
||||
-- This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ).
|
||||
--
|
||||
-- ## 1.4) Apply additional Filters to fine-tune the detected objects
|
||||
-- ## **Visual filters** to fine-tune the probability of the detected objects
|
||||
--
|
||||
-- By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means.
|
||||
-- That being said, the DCS World detection algorithm can sometimes be unrealistic.
|
||||
@ -88,7 +135,8 @@ do -- DETECTION_BASE
|
||||
-- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters.
|
||||
-- Only when you experience unrealistic behaviour in your missions, these filters could be applied.
|
||||
--
|
||||
-- ### 1.4.1 ) Distance visual detection probability
|
||||
--
|
||||
-- ### Distance visual detection probability
|
||||
--
|
||||
-- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly.
|
||||
-- Also, the speed of accurate detection plays a role.
|
||||
@ -102,7 +150,7 @@ do -- DETECTION_BASE
|
||||
--
|
||||
-- Use the method @{Detection#DETECTION_BASE.SetDistanceProbability}() to set the probability factor upon a 10 km distance.
|
||||
--
|
||||
-- ### 1.4.2 ) Alpha Angle visual detection probability
|
||||
-- ### Alpha Angle visual detection probability
|
||||
--
|
||||
-- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly.
|
||||
-- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct.
|
||||
@ -114,7 +162,7 @@ do -- DETECTION_BASE
|
||||
--
|
||||
-- Use the method @{Detection#DETECTION_BASE.SetAlphaAngleProbability}() to set the probability factor if 0°.
|
||||
--
|
||||
-- ### 1.4.3 ) Cloudy Zones detection probability
|
||||
-- ### Cloudy Zones detection probability
|
||||
--
|
||||
-- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully.
|
||||
-- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission
|
||||
@ -129,12 +177,12 @@ do -- DETECTION_BASE
|
||||
-- Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for
|
||||
-- AI not to detect so easily targets within a forrest or village rich area.
|
||||
--
|
||||
-- ## 1.5 ) Accept / Reject detected units
|
||||
-- ## Accept / Reject detected units
|
||||
--
|
||||
-- DETECTION_BASE can accept or reject successful detections based on the location of the detected object,
|
||||
-- if it is located in range or located inside or outside of specific zones.
|
||||
--
|
||||
-- ### 1.5.1 ) Detection acceptance of within range limit
|
||||
-- ### Detection acceptance of within range limit
|
||||
--
|
||||
-- A range can be set that will limit a successful detection for a unit.
|
||||
-- Use the method @{Detection#DETECTION_BASE.SetAcceptRange}() to apply a range in meters till where detected units will be accepted.
|
||||
@ -151,7 +199,7 @@ do -- DETECTION_BASE
|
||||
-- Detection:Start()
|
||||
--
|
||||
--
|
||||
-- ### 1.5.2 ) Detection acceptance if within zone(s).
|
||||
-- ### Detection acceptance if within zone(s).
|
||||
--
|
||||
-- Specific ZONE_BASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONE_BASE object(s).
|
||||
-- Use the method @{Detection#DETECTION_BASE.SetAcceptZones}() will accept detected units if they are within the specified zones.
|
||||
@ -171,7 +219,7 @@ do -- DETECTION_BASE
|
||||
-- -- Start the Detection.
|
||||
-- Detection:Start()
|
||||
--
|
||||
-- ### 1.5.3 ) Detection rejectance if within zone(s).
|
||||
-- ### Detection rejectance if within zone(s).
|
||||
--
|
||||
-- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s).
|
||||
-- Use the method @{Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones.
|
||||
@ -192,29 +240,24 @@ do -- DETECTION_BASE
|
||||
-- -- Start the Detection.
|
||||
-- Detection:Start()
|
||||
--
|
||||
-- ## 1.6) DETECTION_BASE is a Finite State Machine
|
||||
-- ## DETECTION_BASE is a Finite State Machine
|
||||
--
|
||||
-- Various Events and State Transitions can be tailored using DETECTION_BASE.
|
||||
--
|
||||
-- ### 1.6.1) DETECTION_BASE States
|
||||
-- ### DETECTION_BASE States
|
||||
--
|
||||
-- * **Detecting**: The detection is running.
|
||||
-- * **Stopped**: The detection is stopped.
|
||||
--
|
||||
-- ### 1.6.2) DETECTION_BASE Events
|
||||
-- ### DETECTION_BASE Events
|
||||
--
|
||||
-- * **Start**: Start the detection process.
|
||||
-- * **Detect**: Detect new units.
|
||||
-- * **Detected**: New units have been detected.
|
||||
-- * **Stop**: Stop the detection process.
|
||||
--
|
||||
-- @field #DETECTION_BASE DETECTION_BASE
|
||||
--
|
||||
-- @type DETECTION_BASE
|
||||
-- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role.
|
||||
-- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected.
|
||||
-- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects.
|
||||
-- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified.
|
||||
-- @field #number DetectionRun
|
||||
-- @extends Core.Fsm#FSM
|
||||
DETECTION_BASE = {
|
||||
ClassName = "DETECTION_BASE",
|
||||
DetectionSetGroup = nil,
|
||||
@ -267,11 +310,19 @@ do -- DETECTION_BASE
|
||||
self.DetectionInterval = 30
|
||||
|
||||
self:InitDetectVisual( true )
|
||||
self:InitDetectOptical( false )
|
||||
self:InitDetectRadar( false )
|
||||
self:InitDetectRWR( false )
|
||||
self:InitDetectIRST( false )
|
||||
self:InitDetectDLINK( false )
|
||||
self:InitDetectOptical( true )
|
||||
self:InitDetectRadar( true )
|
||||
self:InitDetectRWR( true )
|
||||
self:InitDetectIRST( true )
|
||||
self:InitDetectDLINK( true )
|
||||
|
||||
self:FilterCategories( {
|
||||
Unit.Category.AIRPLANE,
|
||||
Unit.Category.GROUND_UNIT,
|
||||
Unit.Category.HELICOPTER,
|
||||
Unit.Category.SHIP,
|
||||
Unit.Category.STRUCTURE
|
||||
} )
|
||||
|
||||
-- Create FSM transitions.
|
||||
|
||||
@ -498,9 +549,8 @@ do -- DETECTION_BASE
|
||||
|
||||
for DetectionObjectID, Detection in pairs( DetectedTargets ) do
|
||||
local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object
|
||||
self:T2( DetectedObject )
|
||||
|
||||
if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then
|
||||
if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then -- and ( DetectedObject:getCategory() == Object.Category.UNIT or DetectedObject:getCategory() == Object.Category.STATIC ) then
|
||||
|
||||
local DetectionAccepted = true
|
||||
|
||||
@ -515,10 +565,14 @@ do -- DETECTION_BASE
|
||||
( DetectedObjectVec3.y - DetectionGroupVec3.y )^2 +
|
||||
( DetectedObjectVec3.z - DetectionGroupVec3.z )^2
|
||||
) ^ 0.5 / 1000
|
||||
|
||||
local DetectedUnitCategory = DetectedObject:getDesc().category
|
||||
|
||||
self:T( { "Detected Target", DetectionGroupName, DetectedObjectName, Distance } )
|
||||
|
||||
self:T( { "Detected Target:", DetectionGroupName, DetectedObjectName, Distance, DetectedUnitCategory, DetectedCategory } )
|
||||
|
||||
-- Calculate Acceptance
|
||||
|
||||
DetectionAccepted = self._.FilterCategories[DetectedUnitCategory] ~= nil and DetectionAccepted or false
|
||||
|
||||
if self.AcceptRange and Distance > self.AcceptRange then
|
||||
DetectionAccepted = false
|
||||
@ -625,10 +679,10 @@ do -- DETECTION_BASE
|
||||
end
|
||||
|
||||
if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then
|
||||
self:__Detect( self.DetectionInterval )
|
||||
|
||||
self:T( "--> Create Detection Sets" )
|
||||
self:CreateDetectionSets()
|
||||
|
||||
self:__Detect( self.DetectionInterval )
|
||||
end
|
||||
|
||||
end
|
||||
@ -645,6 +699,8 @@ do -- DETECTION_BASE
|
||||
function DETECTION_BASE:InitDetectVisual( DetectVisual )
|
||||
|
||||
self.DetectVisual = DetectVisual
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Detect Optical.
|
||||
@ -655,6 +711,8 @@ do -- DETECTION_BASE
|
||||
self:F2()
|
||||
|
||||
self.DetectOptical = DetectOptical
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Detect Radar.
|
||||
@ -665,6 +723,8 @@ do -- DETECTION_BASE
|
||||
self:F2()
|
||||
|
||||
self.DetectRadar = DetectRadar
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Detect IRST.
|
||||
@ -675,6 +735,8 @@ do -- DETECTION_BASE
|
||||
self:F2()
|
||||
|
||||
self.DetectIRST = DetectIRST
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Detect RWR.
|
||||
@ -685,6 +747,8 @@ do -- DETECTION_BASE
|
||||
self:F2()
|
||||
|
||||
self.DetectRWR = DetectRWR
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Detect DLINK.
|
||||
@ -695,9 +759,52 @@ do -- DETECTION_BASE
|
||||
self:F2()
|
||||
|
||||
self.DetectDLINK = DetectDLINK
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
do -- Filter methods
|
||||
|
||||
--- Filter the detected units based on Unit.Category
|
||||
-- The different values of Unit.Category can be:
|
||||
--
|
||||
-- * Unit.Category.AIRPLANE
|
||||
-- * Unit.Category.GROUND_UNIT
|
||||
-- * Unit.Category.HELICOPTER
|
||||
-- * Unit.Category.SHIP
|
||||
-- * Unit.Category.STRUCTURE
|
||||
--
|
||||
-- Multiple Unit.Category entries can be given as a table and then these will be evaluated as an OR expression.
|
||||
--
|
||||
-- Example to filter a single category (Unit.Category.AIRPLANE).
|
||||
--
|
||||
-- DetectionObject:FilterCategories( Unit.Category.AIRPLANE )
|
||||
--
|
||||
-- Example to filter multiple categories (Unit.Category.AIRPLANE, Unit.Category.HELICOPTER). Note the {}.
|
||||
--
|
||||
-- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
|
||||
--
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #list<Dcs.DCSUnit#Unit> FilterCategories The Categories entries
|
||||
-- @return #DETECTION_BASE self
|
||||
function DETECTION_BASE:FilterCategories( FilterCategories )
|
||||
self:F2()
|
||||
|
||||
self._.FilterCategories = {}
|
||||
if type( FilterCategories ) == "table" then
|
||||
for CategoryID, Category in pairs( FilterCategories ) do
|
||||
self._.FilterCategories[Category] = Category
|
||||
end
|
||||
else
|
||||
self._.FilterCategories[FilterCategories] = FilterCategories
|
||||
end
|
||||
return self
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
do
|
||||
|
||||
|
||||
@ -1,112 +1,26 @@
|
||||
--- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** --
|
||||
-- **Spawn groups of units dynamically in your missions.**
|
||||
--- **Functional** -- Spawn dynamically new GROUPs in your missions.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
-- ====
|
||||
--
|
||||
-- # 1) @{#SPAWN} class, extends @{Base#BASE}
|
||||
-- The documentation of the SPAWN class can be found further in this document.
|
||||
--
|
||||
-- The @{#SPAWN} class allows to spawn dynamically new groups, based on pre-defined initialization settings, modifying the behaviour when groups are spawned.
|
||||
-- For each group to be spawned, within the mission editor, a group has to be created with the "late activation flag" set. We call this group the *"Spawn Template"* of the SPAWN object.
|
||||
-- A reference to this Spawn Template needs to be provided when constructing the SPAWN object, by indicating the name of the group within the mission editor in the constructor methods.
|
||||
-- ====
|
||||
--
|
||||
-- Within the SPAWN object, there is an internal index that keeps track of which group from the internal group list was spawned.
|
||||
-- When new groups get spawned by using the SPAWN methods (see below), it will be validated whether the Limits (@{#SPAWN.Limit}) of the SPAWN object are not reached.
|
||||
-- When all is valid, a new group will be created by the spawning methods, and the internal index will be increased with 1.
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- Regarding the name of new spawned groups, a _SpawnPrefix_ will be assigned for each new group created.
|
||||
-- If you want to have the Spawn Template name to be used as the _SpawnPrefix_ name, use the @{#SPAWN.New} constructor.
|
||||
-- However, when the @{#SPAWN.NewWithAlias} constructor was used, the Alias name will define the _SpawnPrefix_ name.
|
||||
-- Groups will follow the following naming structure when spawned at run-time:
|
||||
-- ### [SPAWN Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPA%20-%20Spawning)
|
||||
--
|
||||
-- 1. Spawned groups will have the name _SpawnPrefix_#ggg, where ggg is a counter from 0 to 999.
|
||||
-- 2. Spawned units will have the name _SpawnPrefix_#ggg-uu, where uu is a counter from 0 to 99 for each new spawned unit belonging to the group.
|
||||
--
|
||||
-- Some additional notes that need to be remembered:
|
||||
--
|
||||
-- * Templates are actually groups defined within the mission editor, with the flag "Late Activation" set. As such, these groups are never used within the mission, but are used by the @{#SPAWN} module.
|
||||
-- * It is important to defined BEFORE you spawn new groups, a proper initialization of the SPAWN instance is done with the options you want to use.
|
||||
-- * When designing a mission, NEVER name groups using a "#" within the name of the group Spawn Template(s), or the SPAWN module logic won't work anymore.
|
||||
--
|
||||
-- ## 1.1) SPAWN construction methods
|
||||
--
|
||||
-- Create a new SPAWN object with the @{#SPAWN.New}() or the @{#SPAWN.NewWithAlias}() methods:
|
||||
--
|
||||
-- * @{#SPAWN.New}(): Creates a new SPAWN object taking the name of the group that represents the GROUP Template (definition).
|
||||
-- * @{#SPAWN.NewWithAlias}(): Creates a new SPAWN object taking the name of the group that represents the GROUP Template (definition), and gives each spawned @{Group} an different name.
|
||||
-- ### [SPAWN Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPA%20-%20Spawning)
|
||||
--
|
||||
-- It is important to understand how the SPAWN class works internally. The SPAWN object created will contain internally a list of groups that will be spawned and that are already spawned.
|
||||
-- The initialization methods will modify this list of groups so that when a group gets spawned, ALL information is already prepared when spawning. This is done for performance reasons.
|
||||
-- So in principle, the group list will contain all parameters and configurations after initialization, and when groups get actually spawned, this spawning can be done quickly and efficient.
|
||||
--
|
||||
-- ## 1.2) SPAWN initialization methods
|
||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||
--
|
||||
-- A spawn object will behave differently based on the usage of **initialization** methods, which all start with the **Init** prefix:
|
||||
-- ====
|
||||
--
|
||||
-- * @{#SPAWN.InitKeepUnitNames}(): Keeps the unit names as defined within the mission editor, but note that anything after a # mark is ignored, and any spaces before and after the resulting name are removed. IMPORTANT! This method MUST be the first used after :New !!!
|
||||
-- * @{#SPAWN.InitLimit}(): Limits the amount of groups that can be alive at the same time and that can be dynamically spawned.
|
||||
-- * @{#SPAWN.InitRandomizeRoute}(): Randomize the routes of spawned groups, and for air groups also optionally the height.
|
||||
-- * @{#SPAWN.InitRandomizeTemplate}(): Randomize the group templates so that when a new group is spawned, a random group template is selected from one of the templates defined.
|
||||
-- * @{#SPAWN.InitUnControlled}(): Spawn plane groups uncontrolled.
|
||||
-- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a batallion in an array.
|
||||
-- * @{#SPAWN.InitRepeat}(): Re-spawn groups when they land at the home base. Similar methods are @{#SPAWN.InitRepeatOnLanding} and @{#SPAWN.InitRepeatOnEngineShutDown}.
|
||||
-- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens.
|
||||
-- * @{#SPAWN.InitRandomizeUnits}(): Randomizes the @{Unit}s in the @{Group} that is spawned within a **radius band**, given an Outer and Inner radius.
|
||||
-- * @{#SPAWN.InitRandomizeZones}(): Randomizes the spawning between a predefined list of @{Zone}s that are declared using this function. Each zone can be given a probability factor.
|
||||
-- * @{#SPAWN.InitAIOn}(): Turns the AI On when spawning the new @{Group} object.
|
||||
-- * @{#SPAWN.InitAIOff}(): Turns the AI Off when spawning the new @{Group} object.
|
||||
-- * @{#SPAWN.InitAIOnOff}(): Turns the AI On or Off when spawning the new @{Group} object.
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ## 1.3) SPAWN spawning methods
|
||||
--
|
||||
-- Groups can be spawned at different times and methods:
|
||||
--
|
||||
-- * @{#SPAWN.Spawn}(): Spawn one new group based on the last spawned index.
|
||||
-- * @{#SPAWN.ReSpawn}(): Re-spawn a group based on a given index.
|
||||
-- * @{#SPAWN.SpawnScheduled}(): Spawn groups at scheduled but randomized intervals. You can use @{#SPAWN.SpawnScheduleStart}() and @{#SPAWN.SpawnScheduleStop}() to start and stop the schedule respectively.
|
||||
-- * @{#SPAWN.SpawnFromVec3}(): Spawn a new group from a Vec3 coordinate. (The group will can be spawned at a point in the air).
|
||||
-- * @{#SPAWN.SpawnFromVec2}(): Spawn a new group from a Vec2 coordinate. (The group will be spawned at land height ).
|
||||
-- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}.
|
||||
-- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Unit}.
|
||||
-- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}.
|
||||
--
|
||||
-- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object.
|
||||
-- You can use the @{GROUP} object to do further actions with the DCSGroup.
|
||||
--
|
||||
-- ## 1.4) Retrieve alive GROUPs spawned by the SPAWN object
|
||||
--
|
||||
-- The SPAWN class administers which GROUPS it has reserved (in stock) or has created during mission execution.
|
||||
-- Every time a SPAWN object spawns a new GROUP object, a reference to the GROUP object is added to an internal table of GROUPS.
|
||||
-- SPAWN provides methods to iterate through that internal GROUP object reference table:
|
||||
--
|
||||
-- * @{#SPAWN.GetFirstAliveGroup}(): Will find the first alive GROUP it has spawned, and return the alive GROUP object and the first Index where the first alive GROUP object has been found.
|
||||
-- * @{#SPAWN.GetNextAliveGroup}(): Will find the next alive GROUP object from a given Index, and return a reference to the alive GROUP object and the next Index where the alive GROUP has been found.
|
||||
-- * @{#SPAWN.GetLastAliveGroup}(): Will find the last alive GROUP object, and will return a reference to the last live GROUP object and the last Index where the last alive GROUP object has been found.
|
||||
--
|
||||
-- You can use the methods @{#SPAWN.GetFirstAliveGroup}() and sequently @{#SPAWN.GetNextAliveGroup}() to iterate through the alive GROUPS within the SPAWN object, and to actions... See the respective methods for an example.
|
||||
-- The method @{#SPAWN.GetGroupFromIndex}() will return the GROUP object reference from the given Index, dead or alive...
|
||||
--
|
||||
-- ## 1.5) SPAWN object cleaning
|
||||
--
|
||||
-- Sometimes, it will occur during a mission run-time, that ground or especially air objects get damaged, and will while being damged stop their activities, while remaining alive.
|
||||
-- In such cases, the SPAWN object will just sit there and wait until that group gets destroyed, but most of the time it won't,
|
||||
-- and it may occur that no new groups are or can be spawned as limits are reached.
|
||||
-- To prevent this, a @{#SPAWN.InitCleanUp}() initialization method has been defined that will silently monitor the status of each spawned group.
|
||||
-- Once a group has a velocity = 0, and has been waiting for a defined interval, that group will be cleaned or removed from run-time.
|
||||
-- There is a catch however :-) If a damaged group has returned to an airbase within the coalition, that group will not be considered as "lost"...
|
||||
-- In such a case, when the inactive group is cleaned, a new group will Re-spawned automatically.
|
||||
-- This models AI that has succesfully returned to their airbase, to restart their combat activities.
|
||||
-- Check the @{#SPAWN.InitCleanUp}() for further info.
|
||||
--
|
||||
-- ## 1.6) Catch the @{Group} spawn event in a callback function!
|
||||
--
|
||||
-- When using the SpawnScheduled method, new @{Group}s are created following the schedule timing parameters.
|
||||
-- When a new @{Group} is spawned, you maybe want to execute actions with that group spawned at the spawn event.
|
||||
-- To SPAWN class supports this functionality through the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method, which takes a function as a parameter that you can define locally.
|
||||
-- Whenever a new @{Group} is spawned, the given function is called, and the @{Group} that was just spawned, is given as a parameter.
|
||||
-- As a result, your spawn event handling function requires one parameter to be declared, which will contain the spawned @{Group} object.
|
||||
-- A coding example is provided at the description of the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method.
|
||||
-- ### [SPAWN YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1jirWIo4t4YxqN-HxjqRkL)
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
@ -119,44 +33,34 @@
|
||||
--
|
||||
-- Hereby the change log:
|
||||
--
|
||||
-- 2017-04-08: SPAWN:**InitDelayOnOff( DelayOnOff )** added.
|
||||
-- 2017-04-08: SPAWN:**InitDelayOn()** added.
|
||||
-- 2017-04-08: SPAWN:**InitDelayOff()** added.
|
||||
--
|
||||
-- 2017-03-14: SPAWN:**InitKeepUnitNames()** added.
|
||||
-- 2017-03-14: SPAWN:**InitRandomizePosition( RandomizePosition, OuterRadious, InnerRadius )** added.
|
||||
-- 2017-03-14: SPAWN:**InitRandomizePosition( RandomizePosition, OuterRadious, InnerRadius )** added.
|
||||
--
|
||||
-- 2017-02-04: SPAWN:InitUnControlled( **UnControlled** ) replaces SPAWN:InitUnControlled().
|
||||
-- 2017-02-04: SPAWN:InitUnControlled( **UnControlled** ) replaces SPAWN:InitUnControlled().
|
||||
--
|
||||
-- 2017-01-24: SPAWN:**InitAIOnOff( AIOnOff )** added.
|
||||
--
|
||||
-- 2017-01-24: SPAWN:**InitAIOn()** added.
|
||||
--
|
||||
-- 2017-01-24: SPAWN:**InitAIOff()** added.
|
||||
--
|
||||
-- 2016-08-15: SPAWN:**InitCleanUp**( SpawnCleanUpInterval ) replaces SPAWN:_CleanUp_( SpawnCleanUpInterval ).
|
||||
-- 2017-01-24: SPAWN:**InitAIOnOff( AIOnOff )** added.
|
||||
-- 2017-01-24: SPAWN:**InitAIOn()** added.
|
||||
-- 2017-01-24: SPAWN:**InitAIOff()** added.
|
||||
--
|
||||
-- 2016-08-15: SPAWN:**InitCleanUp**( SpawnCleanUpInterval ) replaces SPAWN:_CleanUp_( SpawnCleanUpInterval ).
|
||||
-- 2016-08-15: SPAWN:**InitRandomizeZones( SpawnZones )** added.
|
||||
--
|
||||
-- 2016-08-14: SPAWN:**OnSpawnGroup**( SpawnCallBackFunction, ... ) replaces SPAWN:_SpawnFunction_( SpawnCallBackFunction, ... ).
|
||||
--
|
||||
-- 2016-08-14: SPAWN.SpawnInZone( Zone, __RandomizeGroup__, SpawnIndex ) replaces SpawnInZone( Zone, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ).
|
||||
--
|
||||
-- 2016-08-14: SPAWN.SpawnFromVec3( Vec3, SpawnIndex ) replaces SpawnFromVec3( Vec3, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ):
|
||||
--
|
||||
-- 2016-08-14: SPAWN.SpawnFromVec2( Vec2, SpawnIndex ) replaces SpawnFromVec2( Vec2, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ):
|
||||
--
|
||||
-- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromUnit( SpawnUnit, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ):
|
||||
--
|
||||
-- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromStatic( SpawnStatic, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ):
|
||||
--
|
||||
-- 2016-08-14: SPAWN.**InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )** added:
|
||||
--
|
||||
-- 2016-08-14: SPAWN.**Init**Limit( SpawnMaxUnitsAlive, SpawnMaxGroups ) replaces SPAWN._Limit_( SpawnMaxUnitsAlive, SpawnMaxGroups ):
|
||||
--
|
||||
-- 2016-08-14: SPAWN.**Init**Array( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) replaces SPAWN._Array_( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ).
|
||||
--
|
||||
-- 2016-08-14: SPAWN.**Init**RandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ) replaces SPAWN._RandomizeRoute_( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ).
|
||||
--
|
||||
-- 2016-08-14: SPAWN.**Init**RandomizeTemplate( SpawnTemplatePrefixTable ) replaces SPAWN._RandomizeTemplate_( SpawnTemplatePrefixTable ).
|
||||
--
|
||||
-- 2016-08-14: SPAWN.**Init**UnControlled() replaces SPAWN._UnControlled_().
|
||||
-- 2016-08-14: SPAWN:**OnSpawnGroup**( SpawnCallBackFunction, ... ) replaces SPAWN:_SpawnFunction_( SpawnCallBackFunction, ... ).
|
||||
-- 2016-08-14: SPAWN.SpawnInZone( Zone, __RandomizeGroup__, SpawnIndex ) replaces SpawnInZone( Zone, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ).
|
||||
-- 2016-08-14: SPAWN.SpawnFromVec3( Vec3, SpawnIndex ) replaces SpawnFromVec3( Vec3, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ).
|
||||
-- 2016-08-14: SPAWN.SpawnFromVec2( Vec2, SpawnIndex ) replaces SpawnFromVec2( Vec2, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ).
|
||||
-- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromUnit( SpawnUnit, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ).
|
||||
-- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromStatic( SpawnStatic, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ).
|
||||
-- 2016-08-14: SPAWN.**InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )** added.
|
||||
-- 2016-08-14: SPAWN.**Init**Limit( SpawnMaxUnitsAlive, SpawnMaxGroups ) replaces SPAWN._Limit_( SpawnMaxUnitsAlive, SpawnMaxGroups ).
|
||||
-- 2016-08-14: SPAWN.**Init**Array( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) replaces SPAWN._Array_( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ).
|
||||
-- 2016-08-14: SPAWN.**Init**RandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ) replaces SPAWN._RandomizeRoute_( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ).
|
||||
-- 2016-08-14: SPAWN.**Init**RandomizeTemplate( SpawnTemplatePrefixTable ) replaces SPAWN._RandomizeTemplate_( SpawnTemplatePrefixTable ).
|
||||
-- 2016-08-14: SPAWN.**Init**UnControlled() replaces SPAWN._UnControlled_().
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@ -177,7 +81,6 @@
|
||||
|
||||
--- SPAWN Class
|
||||
-- @type SPAWN
|
||||
-- @extends Core.Base#BASE
|
||||
-- @field ClassName
|
||||
-- @field #string SpawnTemplatePrefix
|
||||
-- @field #string SpawnAliasPrefix
|
||||
@ -186,6 +89,214 @@
|
||||
-- @field #number SpawnIndex
|
||||
-- @field #number MaxAliveGroups
|
||||
-- @field #SPAWN.SpawnZoneTable SpawnZoneTable
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- # SPAWN class, extends @{Base#BASE}
|
||||
--
|
||||
-- The SPAWN class allows to spawn dynamically new groups.
|
||||
-- Each SPAWN object needs to be have a related **template group** setup in the Mission Editor (ME),
|
||||
-- which is a normal group with the **Late Activation** flag set.
|
||||
-- This template group will never be activated in your mission.
|
||||
-- SPAWN uses that **template group** to reference to all the characteristics
|
||||
-- (air, ground, livery, unit composition, formation, skill level etc) of each new group to be spawned.
|
||||
--
|
||||
-- Therefore, when creating a SPAWN object, the @{#SPAWN.New} and @{#SPAWN.NewWithAlias} require
|
||||
-- **the name of the template group** to be given as a string to those constructor methods.
|
||||
--
|
||||
-- Initialization settings can be applied on the SPAWN object,
|
||||
-- which modify the behaviour or the way groups are spawned.
|
||||
-- These initialization methods have the prefix **Init**.
|
||||
-- There are also spawn methods with the prefix **Spawn** and will spawn new groups in various ways.
|
||||
--
|
||||
-- ### IMPORTANT! The methods with prefix **Init** must be used before any methods with prefix **Spawn** method are used, or unexpected results may appear!!!
|
||||
--
|
||||
-- Because SPAWN can spawn multiple groups of a template group,
|
||||
-- SPAWN has an **internal index** that keeps track
|
||||
-- which was the latest group that was spawned.
|
||||
--
|
||||
-- **Limits** can be set on how many groups can be spawn in each SPAWN object,
|
||||
-- using the method @{#SPAWN.InitLimit}. SPAWN has 2 kind of limits:
|
||||
--
|
||||
-- * The maximum amount of @{Unit}s that can be **alive** at the same time...
|
||||
-- * The maximum amount of @{Group}s that can be **spawned**... This is more of a **resource**-type of limit.
|
||||
--
|
||||
-- When new groups get spawned using the **Spawn** methods,
|
||||
-- it will be evaluated whether any limits have been reached.
|
||||
-- When no spawn limit is reached, a new group will be created by the spawning methods,
|
||||
-- and the internal index will be increased with 1.
|
||||
--
|
||||
-- These limits ensure that your mission does not accidentally get flooded with spawned groups.
|
||||
-- Additionally, it also guarantees that independent of the group composition,
|
||||
-- at any time, the most optimal amount of groups are alive in your mission.
|
||||
-- For example, if your template group has a group composition of 10 units, and you specify a limit of 100 units alive at the same time,
|
||||
-- with unlimited resources = :InitLimit( 100, 0 ) and 10 groups are alive, but two groups have only one unit alive in the group,
|
||||
-- then a sequent Spawn(Scheduled) will allow a new group to be spawned!!!
|
||||
--
|
||||
-- ### IMPORTANT!! If a limit has been reached, it is possible that a **Spawn** method returns **nil**, meaning, no @{Group} had been spawned!!!
|
||||
--
|
||||
-- Spawned groups get **the same name** as the name of the template group.
|
||||
-- Spawned units in those groups keep _by default_ **the same name** as the name of the template group.
|
||||
-- However, because multiple groups and units are created from the template group,
|
||||
-- a suffix is added to each spawned group and unit.
|
||||
--
|
||||
-- Newly spawned groups will get the following naming structure at run-time:
|
||||
--
|
||||
-- 1. Spawned groups will have the name _GroupName_#_nnn_, where _GroupName_ is the name of the **template group**,
|
||||
-- and _nnn_ is a **counter from 0 to 999**.
|
||||
-- 2. Spawned units will have the name _GroupName_#_nnn_-_uu_,
|
||||
-- where _uu_ is a **counter from 0 to 99** for each new spawned unit belonging to the group.
|
||||
--
|
||||
-- That being said, there is a way to keep the same unit names!
|
||||
-- The method @{#SPAWN.InitKeepUnitNames}() will keep the same unit names as defined within the template group, thus:
|
||||
--
|
||||
-- 3. Spawned units will have the name _UnitName_#_nnn_-_uu_,
|
||||
-- where _UnitName_ is the **unit name as defined in the template group*,
|
||||
-- and _uu_ is a **counter from 0 to 99** for each new spawned unit belonging to the group.
|
||||
--
|
||||
-- Some **additional notes that need to be considered!!**:
|
||||
--
|
||||
-- * templates are actually groups defined within the mission editor, with the flag "Late Activation" set.
|
||||
-- As such, these groups are never used within the mission, but are used by the @{#SPAWN} module.
|
||||
-- * It is important to defined BEFORE you spawn new groups,
|
||||
-- a proper initialization of the SPAWN instance is done with the options you want to use.
|
||||
-- * When designing a mission, NEVER name groups using a "#" within the name of the group Spawn template(s),
|
||||
-- or the SPAWN module logic won't work anymore.
|
||||
--
|
||||
-- ## SPAWN construction methods
|
||||
--
|
||||
-- Create a new SPAWN object with the @{#SPAWN.New}() or the @{#SPAWN.NewWithAlias}() methods:
|
||||
--
|
||||
-- * @{#SPAWN.New}(): Creates a new SPAWN object taking the name of the group that represents the GROUP template (definition).
|
||||
-- * @{#SPAWN.NewWithAlias}(): Creates a new SPAWN object taking the name of the group that represents the GROUP template (definition), and gives each spawned @{Group} an different name.
|
||||
--
|
||||
-- It is important to understand how the SPAWN class works internally. The SPAWN object created will contain internally a list of groups that will be spawned and that are already spawned.
|
||||
-- The initialization methods will modify this list of groups so that when a group gets spawned, ALL information is already prepared when spawning. This is done for performance reasons.
|
||||
-- So in principle, the group list will contain all parameters and configurations after initialization, and when groups get actually spawned, this spawning can be done quickly and efficient.
|
||||
--
|
||||
-- ## SPAWN **Init**ialization methods
|
||||
--
|
||||
-- A spawn object will behave differently based on the usage of **initialization** methods, which all start with the **Init** prefix:
|
||||
--
|
||||
-- ### Unit Names
|
||||
--
|
||||
-- * @{#SPAWN.InitKeepUnitNames}(): Keeps the unit names as defined within the mission editor, but note that anything after a # mark is ignored, and any spaces before and after the resulting name are removed. IMPORTANT! This method MUST be the first used after :New !!!
|
||||
--
|
||||
-- ### Route randomization
|
||||
--
|
||||
-- * @{#SPAWN.InitRandomizeRoute}(): Randomize the routes of spawned groups, and for air groups also optionally the height.
|
||||
--
|
||||
-- ### Group composition randomization
|
||||
--
|
||||
-- * @{#SPAWN.InitRandomizeTemplate}(): Randomize the group templates so that when a new group is spawned, a random group template is selected from one of the templates defined.
|
||||
--
|
||||
-- ### Uncontrolled
|
||||
--
|
||||
-- * @{#SPAWN.InitUnControlled}(): Spawn plane groups uncontrolled.
|
||||
--
|
||||
-- ### Array formation
|
||||
--
|
||||
-- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a batallion in an array.
|
||||
--
|
||||
-- ### Position randomization
|
||||
--
|
||||
-- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens.
|
||||
-- * @{#SPAWN.InitRandomizeUnits}(): Randomizes the @{Unit}s in the @{Group} that is spawned within a **radius band**, given an Outer and Inner radius.
|
||||
-- * @{#SPAWN.InitRandomizeZones}(): Randomizes the spawning between a predefined list of @{Zone}s that are declared using this function. Each zone can be given a probability factor.
|
||||
--
|
||||
-- ### Enable / Disable AI when spawning a new @{Group}
|
||||
--
|
||||
-- * @{#SPAWN.InitAIOn}(): Turns the AI On when spawning the new @{Group} object.
|
||||
-- * @{#SPAWN.InitAIOff}(): Turns the AI Off when spawning the new @{Group} object.
|
||||
-- * @{#SPAWN.InitAIOnOff}(): Turns the AI On or Off when spawning the new @{Group} object.
|
||||
--
|
||||
-- ### Limit scheduled spawning
|
||||
--
|
||||
-- * @{#SPAWN.InitLimit}(): Limits the amount of groups that can be alive at the same time and that can be dynamically spawned.
|
||||
--
|
||||
-- ### Delay initial scheduled spawn
|
||||
--
|
||||
-- * @{#SPAWN.InitDelayOnOff}(): Turns the inital delay On/Off when scheduled spawning the first @{Group} object.
|
||||
-- * @{#SPAWN.InitDelayOn}(): Turns the inital delay On when scheduled spawning the first @{Group} object.
|
||||
-- * @{#SPAWN.InitDelayOff}(): Turns the inital delay Off when scheduled spawning the first @{Group} object.
|
||||
--
|
||||
-- ### Repeat spawned @{Group}s upon landing
|
||||
--
|
||||
-- * @{#SPAWN.InitRepeat}() or @{#SPAWN.InitRepeatOnLanding}(): This method is used to re-spawn automatically the same group after it has landed.
|
||||
-- * @{#SPAWN.InitRepeatOnEngineShutDown}(): This method is used to re-spawn automatically the same group after it has landed and it shuts down the engines at the ramp.
|
||||
--
|
||||
--
|
||||
-- ## SPAWN **Spawn** methods
|
||||
--
|
||||
-- Groups can be spawned at different times and methods:
|
||||
--
|
||||
-- ### **Single** spawning methods
|
||||
--
|
||||
-- * @{#SPAWN.Spawn}(): Spawn one new group based on the last spawned index.
|
||||
-- * @{#SPAWN.ReSpawn}(): Re-spawn a group based on a given index.
|
||||
-- * @{#SPAWN.SpawnFromVec3}(): Spawn a new group from a Vec3 coordinate. (The group will can be spawned at a point in the air).
|
||||
-- * @{#SPAWN.SpawnFromVec2}(): Spawn a new group from a Vec2 coordinate. (The group will be spawned at land height ).
|
||||
-- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}.
|
||||
-- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Unit}.
|
||||
-- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}.
|
||||
--
|
||||
-- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object.
|
||||
-- You can use the @{GROUP} object to do further actions with the DCSGroup.
|
||||
--
|
||||
-- ### **Scheduled** spawning methods
|
||||
--
|
||||
-- * @{#SPAWN.SpawnScheduled}(): Spawn groups at scheduled but randomized intervals.
|
||||
-- * @{#SPAWN.SpawnScheduledStart}(): Start or continue to spawn groups at scheduled time intervals.
|
||||
-- * @{#SPAWN.SpawnScheduledStop}(): Stop the spawning of groups at scheduled time intervals.
|
||||
--
|
||||
--
|
||||
--
|
||||
-- ## Retrieve alive GROUPs spawned by the SPAWN object
|
||||
--
|
||||
-- The SPAWN class administers which GROUPS it has reserved (in stock) or has created during mission execution.
|
||||
-- Every time a SPAWN object spawns a new GROUP object, a reference to the GROUP object is added to an internal table of GROUPS.
|
||||
-- SPAWN provides methods to iterate through that internal GROUP object reference table:
|
||||
--
|
||||
-- * @{#SPAWN.GetFirstAliveGroup}(): Will find the first alive GROUP it has spawned, and return the alive GROUP object and the first Index where the first alive GROUP object has been found.
|
||||
-- * @{#SPAWN.GetNextAliveGroup}(): Will find the next alive GROUP object from a given Index, and return a reference to the alive GROUP object and the next Index where the alive GROUP has been found.
|
||||
-- * @{#SPAWN.GetLastAliveGroup}(): Will find the last alive GROUP object, and will return a reference to the last live GROUP object and the last Index where the last alive GROUP object has been found.
|
||||
--
|
||||
-- You can use the methods @{#SPAWN.GetFirstAliveGroup}() and sequently @{#SPAWN.GetNextAliveGroup}() to iterate through the alive GROUPS within the SPAWN object, and to actions... See the respective methods for an example.
|
||||
-- The method @{#SPAWN.GetGroupFromIndex}() will return the GROUP object reference from the given Index, dead or alive...
|
||||
--
|
||||
-- ## Spawned cleaning of inactive groups
|
||||
--
|
||||
-- Sometimes, it will occur during a mission run-time, that ground or especially air objects get damaged, and will while being damged stop their activities, while remaining alive.
|
||||
-- In such cases, the SPAWN object will just sit there and wait until that group gets destroyed, but most of the time it won't,
|
||||
-- and it may occur that no new groups are or can be spawned as limits are reached.
|
||||
-- To prevent this, a @{#SPAWN.InitCleanUp}() initialization method has been defined that will silently monitor the status of each spawned group.
|
||||
-- Once a group has a velocity = 0, and has been waiting for a defined interval, that group will be cleaned or removed from run-time.
|
||||
-- There is a catch however :-) If a damaged group has returned to an airbase within the coalition, that group will not be considered as "lost"...
|
||||
-- In such a case, when the inactive group is cleaned, a new group will Re-spawned automatically.
|
||||
-- This models AI that has succesfully returned to their airbase, to restart their combat activities.
|
||||
-- Check the @{#SPAWN.InitCleanUp}() for further info.
|
||||
--
|
||||
-- ## Catch the @{Group} Spawn Event in a callback function!
|
||||
--
|
||||
-- When using the @{#SPAWN.SpawnScheduled)() method, new @{Group}s are created following the spawn time interval parameters.
|
||||
-- When a new @{Group} is spawned, you maybe want to execute actions with that group spawned at the spawn event.
|
||||
-- The SPAWN class supports this functionality through the method @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ),
|
||||
-- which takes a function as a parameter that you can define locally.
|
||||
-- Whenever a new @{Group} is spawned, the given function is called, and the @{Group} that was just spawned, is given as a parameter.
|
||||
-- As a result, your spawn event handling function requires one parameter to be declared, which will contain the spawned @{Group} object.
|
||||
-- A coding example is provided at the description of the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method.
|
||||
--
|
||||
-- ## Delay the initial spawning
|
||||
--
|
||||
-- When using the @{#SPAWN.SpawnScheduled)() method, the default behaviour of this method will be that it will spawn the initial (first) @{Group}
|
||||
-- immediately when :SpawnScheduled() is initiated. The methods @{#SPAWN.InitDelayOnOff}() and @{#SPAWN.InitDelayOn}() can be used to
|
||||
-- activate a delay before the first @{Group} is spawned. For completeness, a method @{#SPAWN.InitDelayOff}() is also available, that
|
||||
-- can be used to switch off the initial delay. Because there is no delay by default, this method would only be used when a
|
||||
-- @{#SPAWN.SpawnScheduledStop}() ; @{#SPAWN.SpawnScheduledStart}() sequence would have been used.
|
||||
--
|
||||
--
|
||||
-- @field #SPAWN SPAWN
|
||||
--
|
||||
SPAWN = {
|
||||
ClassName = "SPAWN",
|
||||
SpawnTemplatePrefix = nil,
|
||||
@ -227,6 +338,7 @@ function SPAWN:New( SpawnTemplatePrefix )
|
||||
self.AIOnOff = true -- The AI is on by default when spawning a group.
|
||||
self.SpawnUnControlled = false
|
||||
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
|
||||
self.DelayOnOff = false -- No intial delay when spawning the first group.
|
||||
|
||||
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
|
||||
else
|
||||
@ -270,6 +382,7 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix )
|
||||
self.AIOnOff = true -- The AI is on by default when spawning a group.
|
||||
self.SpawnUnControlled = false
|
||||
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
|
||||
self.DelayOnOff = false -- No intial delay when spawning the first group.
|
||||
|
||||
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
|
||||
else
|
||||
@ -626,6 +739,36 @@ do -- AI methods
|
||||
|
||||
end -- AI methods
|
||||
|
||||
do -- Delay methods
|
||||
--- Turns the Delay On or Off for the first @{Group} scheduled spawning.
|
||||
-- The default value is that for scheduled spawning, there is an initial delay when spawning the first @{Group}.
|
||||
-- @param #SPAWN self
|
||||
-- @param #boolean DelayOnOff A value of true sets the Delay On, a value of false sets the Delay Off.
|
||||
-- @return #SPAWN The SPAWN object
|
||||
function SPAWN:InitDelayOnOff( DelayOnOff )
|
||||
|
||||
self.DelayOnOff = DelayOnOff
|
||||
return self
|
||||
end
|
||||
|
||||
--- Turns the Delay On for the @{Group} when spawning.
|
||||
-- @param #SPAWN self
|
||||
-- @return #SPAWN The SPAWN object
|
||||
function SPAWN:InitDelayOn()
|
||||
|
||||
return self:InitDelayOnOff( true )
|
||||
end
|
||||
|
||||
--- Turns the Delay Off for the @{Group} when spawning.
|
||||
-- @param #SPAWN self
|
||||
-- @return #SPAWN The SPAWN object
|
||||
function SPAWN:InitDelayOff()
|
||||
|
||||
return self:InitDelayOnOff( false )
|
||||
end
|
||||
|
||||
end -- Delay methods
|
||||
|
||||
--- Will spawn a group based on the internal index.
|
||||
-- Note: Uses @{DATABASE} module defined in MOOSE.
|
||||
-- @param #SPAWN self
|
||||
@ -669,6 +812,8 @@ function SPAWN:ReSpawn( SpawnIndex )
|
||||
SpawnGroup:ReSpawnFunction()
|
||||
end
|
||||
|
||||
SpawnGroup:ResetEvents()
|
||||
|
||||
return SpawnGroup
|
||||
end
|
||||
|
||||
@ -787,7 +932,11 @@ function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation )
|
||||
self:F( { SpawnTime, SpawnTimeVariation } )
|
||||
|
||||
if SpawnTime ~= nil and SpawnTimeVariation ~= nil then
|
||||
self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, 1, SpawnTime, SpawnTimeVariation )
|
||||
local InitialDelay = 0
|
||||
if self.DelayOnOff == true then
|
||||
InitialDelay = math.random( SpawnTime - SpawnTime * SpawnTimeVariation, SpawnTime + SpawnTime * SpawnTimeVariation )
|
||||
end
|
||||
self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, InitialDelay, SpawnTime, SpawnTimeVariation )
|
||||
end
|
||||
|
||||
return self
|
||||
@ -795,17 +944,23 @@ end
|
||||
|
||||
--- Will re-start the spawning scheduler.
|
||||
-- Note: This method is only required to be called when the schedule was stopped.
|
||||
-- @param #SPAWN self
|
||||
-- @return #SPAWN
|
||||
function SPAWN:SpawnScheduleStart()
|
||||
self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
self.SpawnScheduler:Start()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Will stop the scheduled spawning scheduler.
|
||||
-- @param #SPAWN self
|
||||
-- @return #SPAWN
|
||||
function SPAWN:SpawnScheduleStop()
|
||||
self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
self.SpawnScheduler:Stop()
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@ -926,7 +1081,7 @@ end
|
||||
function SPAWN:SpawnFromUnit( HostUnit, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, HostUnit, SpawnIndex } )
|
||||
|
||||
if HostUnit and HostUnit:IsAlive() then -- and HostUnit:getUnit(1):inAir() == false then
|
||||
if HostUnit and HostUnit:IsAlive() ~= nil then -- and HostUnit:getUnit(1):inAir() == false then
|
||||
return self:SpawnFromVec3( HostUnit:GetVec3(), SpawnIndex )
|
||||
end
|
||||
|
||||
|
||||
@ -117,6 +117,21 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
|
||||
end
|
||||
)
|
||||
|
||||
-- Handle when a player leaves a slot and goes back to spectators ...
|
||||
-- The PlayerUnit will be UnAssigned from the Task.
|
||||
-- When there is no Unit left running the Task, the Task goes into Abort...
|
||||
self:HandleEvent( EVENTS.MissionEnd,
|
||||
--- @param #TASK self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function( self, EventData )
|
||||
local PlayerUnit = EventData.IniUnit
|
||||
for MissionID, Mission in pairs( self:GetMissions() ) do
|
||||
local Mission = Mission -- Tasking.Mission#MISSION
|
||||
Mission:Stop()
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
-- Handle when a player leaves a slot and goes back to spectators ...
|
||||
-- The PlayerUnit will be UnAssigned from the Task.
|
||||
-- When there is no Unit left running the Task, the Task goes into Abort...
|
||||
@ -127,7 +142,9 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
|
||||
local PlayerUnit = EventData.IniUnit
|
||||
for MissionID, Mission in pairs( self:GetMissions() ) do
|
||||
local Mission = Mission -- Tasking.Mission#MISSION
|
||||
Mission:AbortUnit( PlayerUnit )
|
||||
if Mission:IsOngoing() then
|
||||
Mission:AbortUnit( PlayerUnit )
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
@ -257,8 +274,7 @@ end
|
||||
-- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown.
|
||||
function COMMANDCENTER:MessageToGroup( Message, TaskGroup, Name )
|
||||
|
||||
local Prefix = "@ Group"
|
||||
Prefix = Prefix .. ( Name and " (" .. Name .. "): " or '' )
|
||||
local Prefix = Name and "@ " .. Name .. ": " or "@ " .. TaskGroup:GetCallsign() .. ": "
|
||||
Message = Prefix .. Message
|
||||
self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() )
|
||||
|
||||
|
||||
@ -216,7 +216,7 @@ function TASK:SetUnitProcess( FsmTemplate )
|
||||
end
|
||||
|
||||
--- Add a PlayerUnit to join the Task.
|
||||
-- For each Group within the Task, the Unit is check if it can join the Task.
|
||||
-- For each Group within the Task, the Unit is checked if it can join the Task.
|
||||
-- If the Unit was not part of the Task, false is returned.
|
||||
-- If the Unit is part of the Task, true is returned.
|
||||
-- @param #TASK self
|
||||
@ -275,8 +275,9 @@ function TASK:AbortUnit( PlayerUnit )
|
||||
local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup )
|
||||
self:E( { IsAssignedToGroup = IsAssignedToGroup } )
|
||||
if IsAssignedToGroup then
|
||||
local PlayerName = PlayerUnit:GetPlayerName()
|
||||
self:UnAssignFromUnit( PlayerUnit )
|
||||
self:MessageToGroups( PlayerUnit:GetPlayerName() .. " aborted Task " .. self:GetName() )
|
||||
self:MessageToGroups( PlayerName .. " aborted Task " .. self:GetName() )
|
||||
self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } )
|
||||
if #PlayerGroup:GetUnits() == 1 then
|
||||
self:UnAssignFromGroup( PlayerGroup )
|
||||
@ -284,6 +285,7 @@ function TASK:AbortUnit( PlayerUnit )
|
||||
self:RemoveMenuForGroup( PlayerGroup )
|
||||
end
|
||||
self:Abort()
|
||||
self:PlayerAborted( PlayerUnit )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -72,6 +72,8 @@ do -- TASK_A2G_DISPATCHER
|
||||
self.Detection = Detection
|
||||
self.Mission = Mission
|
||||
|
||||
self.Detection:FilterCategories( Unit.Category.GROUND_UNIT, Unit.Category.SHIP )
|
||||
|
||||
self:AddTransition( "Started", "Assign", "Started" )
|
||||
|
||||
--- OnAfter Transition Handler for Event Assign.
|
||||
|
||||
813
Moose Development/Moose/Tasking/Task_CARGO.lua
Normal file
813
Moose Development/Moose/Tasking/Task_CARGO.lua
Normal file
@ -0,0 +1,813 @@
|
||||
--- **Tasking (Release 2.1)** -- The TASK_CARGO models tasks for players to transport @{Cargo}.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- The Moose framework provides various CARGO classes that allow DCS phisical or logical objects to be transported or sling loaded by Carriers.
|
||||
-- The CARGO_ classes, as part of the moose core, are able to Board, Load, UnBoard and UnLoad cargo between Carrier units.
|
||||
--
|
||||
-- This collection of classes in this module define tasks for human players to handle these cargo objects.
|
||||
-- Cargo can be transported, picked-up, deployed and sling-loaded from and to other places.
|
||||
--
|
||||
-- The following classes are important to consider:
|
||||
--
|
||||
-- * @{#TASK_CARGO_TRANSPORT}: Defines a task for a human player to transport a set of cargo between various zones.
|
||||
--
|
||||
-- ==
|
||||
--
|
||||
-- # **API CHANGE HISTORY**
|
||||
--
|
||||
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
|
||||
--
|
||||
-- * **Added** parts are expressed in bold type face.
|
||||
-- * _Removed_ parts are expressed in italic type face.
|
||||
--
|
||||
-- Hereby the change log:
|
||||
--
|
||||
-- 2017-03-09: Revised version.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Concept, Design & Programming.
|
||||
--
|
||||
-- @module Task_Cargo
|
||||
|
||||
do -- TASK_CARGO
|
||||
|
||||
--- @type TASK_CARGO
|
||||
-- @extends Tasking.Task#TASK
|
||||
|
||||
---
|
||||
-- # TASK_CARGO class, extends @{Task#TASK}
|
||||
--
|
||||
-- ## A flexible tasking system
|
||||
--
|
||||
-- The TASK_CARGO classes provide you with a flexible tasking sytem,
|
||||
-- that allows you to transport cargo of various types between various locations
|
||||
-- and various dedicated deployment zones.
|
||||
--
|
||||
-- The cargo in scope of the TASK_CARGO classes must be explicitly given, and is of type SET_CARGO.
|
||||
-- The SET_CARGO contains a collection of CARGO objects that must be handled by the players in the mission.
|
||||
--
|
||||
--
|
||||
-- ## Task execution experience from the player perspective
|
||||
--
|
||||
-- A human player can join the battle field in a client airborne slot or a ground vehicle within the CA module (ALT-J).
|
||||
-- The player needs to accept the task from the task overview list within the mission, using the radio menus.
|
||||
--
|
||||
-- Once the TASK_CARGO is assigned to the player and accepted by the player, the player will obtain
|
||||
-- an extra **Cargo Handling Radio Menu** that contains the CARGO objects that need to be transported.
|
||||
--
|
||||
-- Each CARGO object has a certain state:
|
||||
--
|
||||
-- * **UnLoaded**: The CARGO is located within the battlefield. It may still need to be transported.
|
||||
-- * **Loaded**: The CARGO is loaded within a Carrier. This can be your air unit, or another air unit, or even a vehicle.
|
||||
-- * **Boarding**: The CARGO is running or moving towards your Carrier for loading.
|
||||
-- * **UnBoarding**: The CARGO is driving or jumping out of your Carrier and moves to a location in the Deployment Zone.
|
||||
--
|
||||
-- Cargo must be transported towards different **Deployment @{Zone}s**.
|
||||
--
|
||||
-- The Cargo Handling Radio Menu system allows to execute **various actions** to handle the cargo.
|
||||
-- In the menu, you'll find for each CARGO, that is part of the scope of the task, various actions that can be completed.
|
||||
-- Depending on the location of your Carrier unit, the menu options will vary.
|
||||
--
|
||||
--
|
||||
-- ## Cargo Pickup and Boarding
|
||||
--
|
||||
-- For cargo boarding, a cargo can only execute the boarding actions if it is within the foreseen **Reporting Range**.
|
||||
-- Therefore, it is important that you steer your Carrier within the Reporting Range,
|
||||
-- so that boarding actions can be executed on the cargo.
|
||||
-- To Pickup and Board cargo, the following menu items will be shown in your carrier radio menu:
|
||||
--
|
||||
-- ### Board Cargo
|
||||
--
|
||||
-- If your Carrier is within the Reporting Range of the cargo, it will allow to pickup the cargo by selecting this menu option.
|
||||
-- Depending on the Cargo type, the cargo will either move to your Carrier or you will receive instructions how to handle the cargo
|
||||
-- pickup. If the cargo moves to your carrier, it will indicate the boarding status.
|
||||
-- Note that multiple units need to board your Carrier, so it is required to await the full boarding process.
|
||||
-- Once the cargo is fully boarded within your Carrier, you will be notified of this.
|
||||
--
|
||||
-- Note that for airborne Carriers, it is required to land first before the Boarding process can be initiated.
|
||||
-- If during boarding the Carrier gets airborne, the boarding process will be cancelled.
|
||||
--
|
||||
-- ## Pickup Cargo
|
||||
--
|
||||
-- If your Carrier is not within the Reporting Range of the cargo, the HQ will guide you to its location.
|
||||
-- Routing information is shown in flight that directs you to the cargo within Reporting Range.
|
||||
-- Upon arrival, the Cargo will contact you and further instructions will be given.
|
||||
-- When your Carrier is airborne, you will receive instructions to land your Carrier.
|
||||
-- The action will not be completed until you've landed your Carrier.
|
||||
--
|
||||
--
|
||||
-- ## Cargo Deploy and UnBoarding
|
||||
--
|
||||
-- Various Deployment Zones can be foreseen in the scope of the Cargo transportation. Each deployment zone can be of varying @{Zone} type.
|
||||
-- The Cargo Handling Radio Menu provides with menu options to execute an action to steer your Carrier to a specific Zone.
|
||||
--
|
||||
-- ### UnBoard Cargo
|
||||
--
|
||||
-- If your Carrier is already within a Deployment Zone,
|
||||
-- then the Cargo Handling Radio Menu allows to **UnBoard** a specific cargo that is
|
||||
-- loaded within your Carrier group into the Deployment Zone.
|
||||
-- Note that the Unboarding process takes a while, as the cargo units (infantry or vehicles) must unload from your Carrier.
|
||||
-- Ensure that you stay at the position or stay on the ground while Unboarding.
|
||||
-- If any unforeseen manoeuvre is done by the Carrier, then the Unboarding will be cancelled.
|
||||
--
|
||||
-- ### Deploy Cargo
|
||||
--
|
||||
-- If your Carrier is not within a Deployment Zone, you'll need to fly towards one.
|
||||
-- Fortunately, the Cargo Handling Radio Menu provides you with menu options to select a specific Deployment Zone to fly towards.
|
||||
-- Once a Deployment Zone has been selected, your Carrier will receive routing information from HQ towards the Deployment Zone center.
|
||||
-- Upon arrival, the HQ will provide you with further instructions.
|
||||
-- When your Carrier is airborne, you will receive instructions to land your Carrier.
|
||||
-- The action will not be completed until you've landed your Carrier!
|
||||
--
|
||||
-- ## Handle TASK_CARGO Events ...
|
||||
--
|
||||
-- The TASK_CARGO classes define @{Cargo} transport tasks,
|
||||
-- based on the tasking capabilities defined in @{Task#TASK}.
|
||||
--
|
||||
-- ### Specific TASK_CARGO Events
|
||||
--
|
||||
-- Specific Cargo Handling event can be captured, that allow to trigger specific actions!
|
||||
--
|
||||
-- * **Boarded**: Triggered when the Cargo has been Boarded into your Carrier.
|
||||
-- * **UnBoarded**: Triggered when the cargo has been Unboarded from your Carrier and has arrived at the Deployment Zone.
|
||||
--
|
||||
-- ### Standard TASK_CARGO Events
|
||||
--
|
||||
-- The TASK_CARGO is implemented using a @{Statemachine#FSM_TASK}, and has the following standard statuses:
|
||||
--
|
||||
-- * **None**: Start of the process.
|
||||
-- * **Planned**: The cargo task is planned.
|
||||
-- * **Assigned**: The cargo task is assigned to a @{Group#GROUP}.
|
||||
-- * **Success**: The cargo task is successfully completed.
|
||||
-- * **Failed**: The cargo task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #TASK_CARGO TASK_CARGO
|
||||
--
|
||||
TASK_CARGO = {
|
||||
ClassName = "TASK_CARGO",
|
||||
}
|
||||
|
||||
--- Instantiates a new TASK_CARGO.
|
||||
-- @param #TASK_CARGO self
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
|
||||
-- @param #string TaskName The name of the Task.
|
||||
-- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported.
|
||||
-- @param #string TaskType The type of Cargo task.
|
||||
-- @return #TASK_CARGO self
|
||||
function TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, TaskType )
|
||||
local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) -- #TASK_CARGO
|
||||
self:F( {Mission, SetGroup, TaskName, SetCargo, TaskType})
|
||||
|
||||
self.SetCargo = SetCargo
|
||||
self.TaskType = TaskType
|
||||
|
||||
self.DeployZones = {} -- setmetatable( {}, { __mode = "v" } ) -- weak table on value
|
||||
|
||||
|
||||
local Fsm = self:GetUnitProcess()
|
||||
|
||||
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } )
|
||||
|
||||
Fsm:AddTransition( { "Assigned", "WaitingForCommand", "ArrivedAtPickup", "ArrivedAtDeploy", "Boarded", "UnBoarded" }, "SelectAction", "WaitingForCommand" )
|
||||
|
||||
Fsm:AddTransition( "WaitingForCommand", "RouteToPickup", "RoutingToPickup" )
|
||||
Fsm:AddProcess ( "RoutingToPickup", "RouteToPickupPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtPickup" } )
|
||||
Fsm:AddTransition( "Arrived", "ArriveAtPickup", "ArrivedAtPickup" )
|
||||
|
||||
Fsm:AddTransition( "WaitingForCommand", "RouteToDeploy", "RoutingToDeploy" )
|
||||
Fsm:AddProcess ( "RoutingToDeploy", "RouteToDeployZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtDeploy" } )
|
||||
Fsm:AddTransition( "Arrived", "ArriveAtDeploy", "ArrivedAtDeploy" )
|
||||
|
||||
Fsm:AddTransition( { "ArrivedAtPickup", "ArrivedAtDeploy", "Landing" }, "Land", "Landing" )
|
||||
Fsm:AddTransition( "Landing", "Landed", "Landed" )
|
||||
|
||||
Fsm:AddTransition( "WaitingForCommand", "PrepareBoarding", "AwaitBoarding" )
|
||||
Fsm:AddTransition( "AwaitBoarding", "Board", "Boarding" )
|
||||
Fsm:AddTransition( "Boarding", "Boarded", "Boarded" )
|
||||
|
||||
Fsm:AddTransition( "WaitingForCommand", "PrepareUnBoarding", "AwaitUnBoarding" )
|
||||
Fsm:AddTransition( "AwaitUnBoarding", "UnBoard", "UnBoarding" )
|
||||
Fsm:AddTransition( "UnBoarding", "UnBoarded", "UnBoarded" )
|
||||
|
||||
|
||||
Fsm:AddTransition( "Deployed", "Success", "Success" )
|
||||
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
|
||||
Fsm:AddTransition( "Failed", "Fail", "Failed" )
|
||||
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onenterWaitingForCommand( TaskUnit, Task )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
if TaskUnit.Menu then
|
||||
TaskUnit.Menu:Remove()
|
||||
end
|
||||
|
||||
TaskUnit.Menu = MENU_GROUP:New( TaskUnit:GetGroup(), Task:GetName() .. " @ " .. TaskUnit:GetName() )
|
||||
|
||||
Task.SetCargo:ForEachCargo(
|
||||
|
||||
--- @param Core.Cargo#CARGO Cargo
|
||||
function( Cargo )
|
||||
if Cargo:IsUnLoaded() then
|
||||
if Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
|
||||
MENU_GROUP_COMMAND:New(
|
||||
TaskUnit:GetGroup(),
|
||||
"Board cargo " .. Cargo.Name,
|
||||
TaskUnit.Menu,
|
||||
self.MenuBoardCargo,
|
||||
self,
|
||||
Cargo
|
||||
)
|
||||
else
|
||||
MENU_GROUP_COMMAND:New(
|
||||
TaskUnit:GetGroup(),
|
||||
"Route to Pickup cargo " .. Cargo.Name,
|
||||
TaskUnit.Menu,
|
||||
self.MenuRouteToPickup,
|
||||
self,
|
||||
Cargo
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if Cargo:IsLoaded() then
|
||||
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
|
||||
if Cargo:IsInZone( DeployZone ) then
|
||||
MENU_GROUP_COMMAND:New(
|
||||
TaskUnit:GetGroup(),
|
||||
"Unboard cargo " .. Cargo.Name,
|
||||
TaskUnit.Menu,
|
||||
self.MenuUnBoardCargo,
|
||||
self,
|
||||
Cargo,
|
||||
DeployZone
|
||||
)
|
||||
else
|
||||
MENU_GROUP_COMMAND:New(
|
||||
TaskUnit:GetGroup(),
|
||||
"Route to Deploy cargo at " .. DeployZoneName,
|
||||
TaskUnit.Menu,
|
||||
self.MenuRouteToDeploy,
|
||||
self,
|
||||
DeployZone
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
)
|
||||
|
||||
self:__SelectAction( -15 )
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:OnLeaveWaitingForCommand( TaskUnit, Task )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
TaskUnit.Menu:Remove()
|
||||
end
|
||||
|
||||
function Fsm:MenuBoardCargo( Cargo )
|
||||
self:__PrepareBoarding( 1.0, Cargo )
|
||||
end
|
||||
|
||||
function Fsm:MenuUnBoardCargo( Cargo, DeployZone )
|
||||
self:__PrepareUnBoarding( 1.0, Cargo, DeployZone )
|
||||
end
|
||||
|
||||
function Fsm:MenuRouteToPickup( Cargo )
|
||||
self:__RouteToPickup( 1.0, Cargo )
|
||||
end
|
||||
|
||||
function Fsm:MenuRouteToDeploy( DeployZone )
|
||||
self:__RouteToDeploy( 1.0, DeployZone )
|
||||
end
|
||||
|
||||
--- Route to Cargo
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterRouteToPickup( TaskUnit, Task, From, Event, To, Cargo )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
|
||||
self.Cargo = Cargo
|
||||
Task:SetCargoPickup( self.Cargo, TaskUnit )
|
||||
self:__RouteToPickupPoint( -0.1 )
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterArriveAtPickup( TaskUnit, Task )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
if TaskUnit:IsAir() then
|
||||
self:__Land( -0.1, "Pickup" )
|
||||
else
|
||||
self:__SelectAction( -0.1 )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Route to DeployZone
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
function Fsm:onafterRouteToDeploy( TaskUnit, Task, From, Event, To, DeployZone )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
|
||||
self.DeployZone = DeployZone
|
||||
Task:SetDeployZone( self.DeployZone, TaskUnit )
|
||||
self:__RouteToDeployZone( -0.1 )
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterArriveAtDeploy( TaskUnit, Task )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
if TaskUnit:IsAir() then
|
||||
self:__Land( -0.1, "Deploy" )
|
||||
else
|
||||
self:__SelectAction( -0.1 )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterLand( TaskUnit, Task, From, Event, To, Action )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
|
||||
if TaskUnit:InAir() then
|
||||
Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() )
|
||||
self:__Land( -10, Action )
|
||||
else
|
||||
Task:GetMission():GetCommandCenter():MessageToGroup( "Landed ...", TaskUnit:GetGroup() )
|
||||
self:__Landed( -0.1, Action )
|
||||
end
|
||||
else
|
||||
if Action == "Pickup" then
|
||||
self:__RouteToPickupZone( -0.1 )
|
||||
else
|
||||
self:__RouteToDeployZone( -0.1 )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterLanded( TaskUnit, Task, From, Event, To, Action )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
|
||||
if TaskUnit:InAir() then
|
||||
self:__Land( -0.1, Action )
|
||||
else
|
||||
self:__SelectAction( -0.1 )
|
||||
end
|
||||
else
|
||||
if Action == "Pickup" then
|
||||
self:__RouteToPickupZone( -0.1 )
|
||||
else
|
||||
self:__RouteToDeployZone( -0.1 )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterPrepareBoarding( TaskUnit, Task, From, Event, To, Cargo )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
self.Cargo = Cargo -- Core.Cargo#CARGO_GROUP
|
||||
self:__Board( -0.1 )
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterBoard( TaskUnit, Task )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
function self.Cargo:OnEnterLoaded( From, Event, To, TaskUnit, TaskProcess )
|
||||
|
||||
self:E({From, Event, To, TaskUnit, TaskProcess })
|
||||
|
||||
TaskProcess:__Boarded( 0.1 )
|
||||
|
||||
end
|
||||
|
||||
|
||||
if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
|
||||
if TaskUnit:InAir() then
|
||||
--- ABORT the boarding. Split group if any and go back to select action.
|
||||
else
|
||||
self.Cargo:MessageToGroup( "Boarding ...", TaskUnit:GetGroup() )
|
||||
self.Cargo:Board( TaskUnit, 20, self )
|
||||
end
|
||||
else
|
||||
--self:__ArriveAtCargo( -0.1 )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterBoarded( TaskUnit, Task )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
self.Cargo:MessageToGroup( "Boarded ...", TaskUnit:GetGroup() )
|
||||
self:__SelectAction( 1 )
|
||||
|
||||
-- TODO:I need to find a more decent solution for this.
|
||||
Task:E( { CargoPickedUp = Task.CargoPickedUp } )
|
||||
if Task.CargoPickedUp then
|
||||
Task:CargoPickedUp( TaskUnit, self.Cargo )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterPrepareUnBoarding( TaskUnit, Task, From, Event, To, Cargo, DeployZone )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
self.Cargo = Cargo
|
||||
self.DeployZone = DeployZone
|
||||
self:__UnBoard( -0.1 )
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterUnBoard( TaskUnit, Task )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
function self.Cargo:OnEnterUnLoaded( From, Event, To, DeployZone, TaskProcess )
|
||||
|
||||
self:E({From, Event, To, TaskUnit, TaskProcess })
|
||||
|
||||
TaskProcess:__UnBoarded( -0.1 )
|
||||
|
||||
end
|
||||
|
||||
self.Cargo:MessageToGroup( "UnBoarding ...", TaskUnit:GetGroup() )
|
||||
self.Cargo:UnBoard( self.DeployZone:GetPointVec2(), 20, self )
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterUnBoarded( TaskUnit, Task )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
self.Cargo:MessageToGroup( "UnBoarded ...", TaskUnit:GetGroup() )
|
||||
|
||||
-- TODO:I need to find a more decent solution for this.
|
||||
Task:E( { CargoDeployed = Task.CargoDeployed } )
|
||||
if Task.CargoDeployed then
|
||||
Task:CargoDeployed( TaskUnit, self.Cargo, self.DeployZone )
|
||||
end
|
||||
|
||||
self:__SelectAction( 1 )
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
|
||||
end
|
||||
|
||||
--- @param #TASK_CARGO self
|
||||
function TASK_CARGO:GetPlannedMenuText()
|
||||
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
|
||||
end
|
||||
|
||||
--- @param #TASK_CARGO self
|
||||
-- @return Core.Set#SET_CARGO The Cargo Set.
|
||||
function TASK_CARGO:GetCargoSet()
|
||||
|
||||
return self.SetCargo
|
||||
end
|
||||
|
||||
--- @param #TASK_CARGO self
|
||||
-- @return #list<Core.Zone#ZONE_BASE> The Deployment Zones.
|
||||
function TASK_CARGO:GetDeployZones()
|
||||
|
||||
return self.DeployZones
|
||||
end
|
||||
|
||||
--- @param #TASK_CARGO self
|
||||
-- @param AI.AI_Cargo#AI_CARGO Cargo The cargo.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @return #TASK_CARGO
|
||||
function TASK_CARGO:SetCargoPickup( Cargo, TaskUnit )
|
||||
|
||||
self:F({Cargo, TaskUnit})
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
local ActRouteCargo = ProcessUnit:GetProcess( "RoutingToPickup", "RouteToPickupPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
||||
ActRouteCargo:SetPointVec2( Cargo:GetPointVec2() )
|
||||
ActRouteCargo:SetRange( Cargo:GetBoardingRange() )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_CARGO self
|
||||
-- @param Core.Zone#ZONE DeployZone
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @return #TASK_CARGO
|
||||
function TASK_CARGO:SetDeployZone( DeployZone, TaskUnit )
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
local ActRouteDeployZone = ProcessUnit:GetProcess( "RoutingToDeploy", "RouteToDeployZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
||||
ActRouteDeployZone:SetZone( DeployZone )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_CARGO self
|
||||
-- @param Core.Zone#ZONE DeployZone
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @return #TASK_CARGO
|
||||
function TASK_CARGO:AddDeployZone( DeployZone, TaskUnit )
|
||||
|
||||
self.DeployZones[DeployZone:GetName()] = DeployZone
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param #TASK_CARGO self
|
||||
-- @param Core.Zone#ZONE DeployZone
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @return #TASK_CARGO
|
||||
function TASK_CARGO:RemoveDeployZone( DeployZone, TaskUnit )
|
||||
|
||||
self.DeployZones[DeployZone:GetName()] = nil
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param #TASK_CARGO self
|
||||
-- @param @list<Core.Zone#ZONE> DeployZones
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @return #TASK_CARGO
|
||||
function TASK_CARGO:SetDeployZones( DeployZones, TaskUnit )
|
||||
|
||||
for DeployZoneID, DeployZone in pairs( DeployZones ) do
|
||||
self.DeployZones[DeployZone:GetName()] = DeployZone
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param #TASK_CARGO self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map.
|
||||
function TASK_CARGO:GetTargetZone( TaskUnit )
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
||||
return ActRouteTarget:GetZone()
|
||||
end
|
||||
|
||||
--- Set a score when a target in scope of the A2G attack, has been destroyed .
|
||||
-- @param #TASK_CARGO self
|
||||
-- @param #string Text The text to display to the player, when the target has been destroyed.
|
||||
-- @param #number Score The score in points.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @return #TASK_CARGO
|
||||
function TASK_CARGO:SetScoreOnDestroy( Text, Score, TaskUnit )
|
||||
self:F( { Text, Score, TaskUnit } )
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScoreProcess( "Engaging", "Account", "Account", Text, Score )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a score when all the targets in scope of the A2G attack, have been destroyed.
|
||||
-- @param #TASK_CARGO self
|
||||
-- @param #string Text The text to display to the player, when all targets hav been destroyed.
|
||||
-- @param #number Score The score in points.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @return #TASK_CARGO
|
||||
function TASK_CARGO:SetScoreOnSuccess( Text, Score, TaskUnit )
|
||||
self:F( { Text, Score, TaskUnit } )
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScore( "Success", Text, Score )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a penalty when the A2G attack has failed.
|
||||
-- @param #TASK_CARGO self
|
||||
-- @param #string Text The text to display to the player, when the A2G attack has failed.
|
||||
-- @param #number Penalty The penalty in points.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @return #TASK_CARGO
|
||||
function TASK_CARGO:SetPenaltyOnFailed( Text, Penalty, TaskUnit )
|
||||
self:F( { Text, Score, TaskUnit } )
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScore( "Failed", Text, Penalty )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
do -- TASK_CARGO_TRANSPORT
|
||||
|
||||
--- The TASK_CARGO_TRANSPORT class
|
||||
-- @type TASK_CARGO_TRANSPORT
|
||||
-- @extends #TASK_CARGO
|
||||
TASK_CARGO_TRANSPORT = {
|
||||
ClassName = "TASK_CARGO_TRANSPORT",
|
||||
}
|
||||
|
||||
--- Instantiates a new TASK_CARGO_TRANSPORT.
|
||||
-- @param #TASK_CARGO_TRANSPORT self
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
|
||||
-- @param #string TaskName The name of the Task.
|
||||
-- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported.
|
||||
-- @return #TASK_CARGO_TRANSPORT self
|
||||
function TASK_CARGO_TRANSPORT:New( Mission, SetGroup, TaskName, SetCargo )
|
||||
local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "Transport" ) ) -- #TASK_CARGO_TRANSPORT
|
||||
self:F()
|
||||
|
||||
Mission:AddTask( self )
|
||||
|
||||
|
||||
-- Events
|
||||
|
||||
self:AddTransition( "*", "CargoPickedUp", "*" )
|
||||
self:AddTransition( "*", "CargoDeployed", "*" )
|
||||
|
||||
do
|
||||
|
||||
--- OnBefore Transition Handler for Event CargoPickedUp.
|
||||
-- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoPickedUp
|
||||
-- @param #TASK_CARGO_TRANSPORT self
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
|
||||
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event CargoPickedUp.
|
||||
-- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoPickedUp
|
||||
-- @param #TASK_CARGO_TRANSPORT self
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
|
||||
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
|
||||
|
||||
--- Synchronous Event Trigger for Event CargoPickedUp.
|
||||
-- @function [parent=#TASK_CARGO_TRANSPORT] CargoPickedUp
|
||||
-- @param #TASK_CARGO_TRANSPORT self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
|
||||
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
|
||||
|
||||
--- Asynchronous Event Trigger for Event CargoPickedUp.
|
||||
-- @function [parent=#TASK_CARGO_TRANSPORT] __CargoPickedUp
|
||||
-- @param #TASK_CARGO_TRANSPORT self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
|
||||
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
|
||||
end
|
||||
|
||||
do
|
||||
--- OnBefore Transition Handler for Event CargoDeployed.
|
||||
-- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoDeployed
|
||||
-- @param #TASK_CARGO_TRANSPORT self
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
|
||||
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event CargoDeployed.
|
||||
-- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoDeployed
|
||||
-- @param #TASK_CARGO_TRANSPORT self
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
|
||||
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
|
||||
|
||||
--- Synchronous Event Trigger for Event CargoDeployed.
|
||||
-- @function [parent=#TASK_CARGO_TRANSPORT] CargoDeployed
|
||||
-- @param #TASK_CARGO_TRANSPORT self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
|
||||
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
|
||||
|
||||
--- Asynchronous Event Trigger for Event CargoDeployed.
|
||||
-- @function [parent=#TASK_CARGO_TRANSPORT] __CargoDeployed
|
||||
-- @param #TASK_CARGO_TRANSPORT self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
|
||||
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
|
||||
end
|
||||
|
||||
local Fsm = self:GetUnitProcess()
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #TASK_CARGO_TRANSPORT self
|
||||
-- @return #boolean
|
||||
function TASK_CARGO_TRANSPORT:IsAllCargoTransported()
|
||||
|
||||
local CargoSet = self:GetCargoSet()
|
||||
local Set = CargoSet:GetSet()
|
||||
|
||||
local DeployZones = self:GetDeployZones()
|
||||
|
||||
local CargoDeployed = true
|
||||
|
||||
-- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ).
|
||||
for CargoID, CargoData in pairs( Set ) do
|
||||
local Cargo = CargoData -- Core.Cargo#CARGO
|
||||
|
||||
-- Loop the DeployZones set for the TASK_CARGO_TRANSPORT.
|
||||
for DeployZoneID, DeployZone in pairs( DeployZones ) do
|
||||
|
||||
-- If there is a Cargo not in one of DeployZones, then not all Cargo is deployed.
|
||||
self:T( { Cargo.CargoObject } )
|
||||
if Cargo:IsInZone( DeployZone ) then
|
||||
else
|
||||
CargoDeployed = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return CargoDeployed
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@ -156,6 +156,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return Dcs.DCSWrapper.Group#Group The DCS Group.
|
||||
function GROUP:GetDCSObject()
|
||||
self:F(self.GroupName)
|
||||
local DCSGroup = Group.getByName( self.GroupName )
|
||||
|
||||
if DCSGroup then
|
||||
@ -319,6 +320,7 @@ function GROUP:GetUnit( UnitNumber )
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
local DCSUnit = DCSGroup:getUnit( UnitNumber )
|
||||
local UnitFound = UNIT:Find( DCSGroup:getUnit( UnitNumber ) )
|
||||
self:T2( UnitFound )
|
||||
return UnitFound
|
||||
@ -834,6 +836,9 @@ function GROUP:Respawn( Template )
|
||||
|
||||
self:Destroy()
|
||||
_DATABASE:Spawn( Template )
|
||||
|
||||
self:ResetEvents()
|
||||
|
||||
end
|
||||
|
||||
--- Returns the group template from the @{DATABASE} (_DATABASE object).
|
||||
@ -1077,7 +1082,21 @@ do -- Event Handling
|
||||
-- @return #GROUP
|
||||
function GROUP:UnHandleEvent( Event )
|
||||
|
||||
self:EventDispatcher():RemoveForGroup( self:GetName(), self, Event )
|
||||
self:EventDispatcher():Remove( self, Event )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Reset the subscriptions.
|
||||
-- @param #GROUP self
|
||||
-- @return #GROUP
|
||||
function GROUP:ResetEvents()
|
||||
|
||||
self:EventDispatcher():Reset( self )
|
||||
|
||||
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
||||
UnitData:ResetEvents()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@ -640,7 +640,7 @@ function UNIT:GetThreatLevel()
|
||||
"Bomber",
|
||||
"Strategic Bomber",
|
||||
"Attack Helicopter",
|
||||
"Interceptor",
|
||||
"Battleplane",
|
||||
"Multirole Fighter",
|
||||
"Fighter"
|
||||
}
|
||||
@ -990,5 +990,16 @@ do -- Event Handling
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Reset the subscriptions.
|
||||
-- @param #UNIT self
|
||||
-- @return #UNIT
|
||||
function UNIT:ResetEvents()
|
||||
|
||||
self:EventDispatcher():Reset( self )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
@ -0,0 +1,31 @@
|
||||
env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' )
|
||||
env.info( 'Moose Generation Timestamp: 20170328_0728' )
|
||||
|
||||
local base = _G
|
||||
|
||||
Include = {}
|
||||
|
||||
Include.File = function( IncludeFile )
|
||||
if not Include.Files[ IncludeFile ] then
|
||||
Include.Files[IncludeFile] = IncludeFile
|
||||
env.info( "Include:" .. IncludeFile .. " from " .. Include.ProgramPath )
|
||||
local f = assert( base.loadfile( Include.ProgramPath .. IncludeFile .. ".lua" ) )
|
||||
if f == nil then
|
||||
error ("Could not load MOOSE file " .. IncludeFile .. ".lua" )
|
||||
else
|
||||
env.info( "Include:" .. IncludeFile .. " loaded from " .. Include.ProgramPath )
|
||||
return f()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Include.ProgramPath = "Scripts/Moose/"
|
||||
|
||||
env.info( "Include.ProgramPath = " .. Include.ProgramPath)
|
||||
|
||||
Include.Files = {}
|
||||
|
||||
Include.File( "Moose" )
|
||||
|
||||
BASE:TraceOnOff( true )
|
||||
env.info( '*** MOOSE INCLUDE END *** ' )
|
||||
@ -13,6 +13,8 @@ Core/Point.lua
|
||||
Core/Message.lua
|
||||
Core/Fsm.lua
|
||||
Core/Radio.lua
|
||||
Core/SpawnStatic.lua
|
||||
Core/Cargo.lua
|
||||
|
||||
Wrapper/Object.lua
|
||||
Wrapper/Identifiable.lua
|
||||
@ -39,7 +41,6 @@ AI/AI_Balancer.lua
|
||||
AI/AI_Patrol.lua
|
||||
AI/AI_Cap.lua
|
||||
AI/AI_Cas.lua
|
||||
AI/AI_Cargo.lua
|
||||
|
||||
Actions/Act_Assign.lua
|
||||
Actions/Act_Route.lua
|
||||
@ -52,5 +53,6 @@ Tasking/Task.lua
|
||||
Tasking/DetectionManager.lua
|
||||
Tasking/Task_A2G_Dispatcher.lua
|
||||
Tasking/Task_A2G.lua
|
||||
Tasking/Task_Cargo.lua
|
||||
|
||||
Moose.lua
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
"C:\Program Files\lua\5.1\bin\lua.exe" -e "package.path=\"C:\\Users\\svenv\\AppData\\Roaming/luarocks/share/lua/5.1/?.lua;C:\\Users\\svenv\\AppData\\Roaming/luarocks/share/lua/5.1/?/init.lua;c:\\program files\\lua\\5.1\\/share/lua/5.1/?.lua;c:\\program files\\lua\\5.1\\/share/lua/5.1/?/init.lua;C:\\Program Files (x86)\\LuaRocks\\lua\\?.lua;\"..package.path; package.cpath=\"C:\\Users\\svenv\\AppData\\Roaming/luarocks/lib/lua/5.1/?.dll;c:\\program files\\lua\\5.1\\/lib/lua/5.1/?.dll;\"..package.cpath" -e "local k,l,_=pcall(require,\"luarocks.loader\") _=k and l.add_context(\"luadocumentor\",\"0.1.5-1\")" "c:\program files\lua\5.1\\lib\luarocks\rocks\luadocumentor\0.1.5-1\bin\luadocumentor" %*
|
||||
exit /b %ERRORLEVEL%
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,163 +0,0 @@
|
||||
.\" $Id: lua.man,v 1.11 2006/01/06 16:03:34 lhf Exp $
|
||||
.TH LUA 1 "$Date: 2006/01/06 16:03:34 $"
|
||||
.SH NAME
|
||||
lua \- Lua interpreter
|
||||
.SH SYNOPSIS
|
||||
.B lua
|
||||
[
|
||||
.I options
|
||||
]
|
||||
[
|
||||
.I script
|
||||
[
|
||||
.I args
|
||||
]
|
||||
]
|
||||
.SH DESCRIPTION
|
||||
.B lua
|
||||
is the stand-alone Lua interpreter.
|
||||
It loads and executes Lua programs,
|
||||
either in textual source form or
|
||||
in precompiled binary form.
|
||||
(Precompiled binaries are output by
|
||||
.BR luac ,
|
||||
the Lua compiler.)
|
||||
.B lua
|
||||
can be used as a batch interpreter and also interactively.
|
||||
.LP
|
||||
The given
|
||||
.I options
|
||||
(see below)
|
||||
are executed and then
|
||||
the Lua program in file
|
||||
.I script
|
||||
is loaded and executed.
|
||||
The given
|
||||
.I args
|
||||
are available to
|
||||
.I script
|
||||
as strings in a global table named
|
||||
.BR arg .
|
||||
If these arguments contain spaces or other characters special to the shell,
|
||||
then they should be quoted
|
||||
(but note that the quotes will be removed by the shell).
|
||||
The arguments in
|
||||
.B arg
|
||||
start at 0,
|
||||
which contains the string
|
||||
.RI ' script '.
|
||||
The index of the last argument is stored in
|
||||
.BR arg.n .
|
||||
The arguments given in the command line before
|
||||
.IR script ,
|
||||
including the name of the interpreter,
|
||||
are available in negative indices in
|
||||
.BR arg .
|
||||
.LP
|
||||
At the very start,
|
||||
before even handling the command line,
|
||||
.B lua
|
||||
executes the contents of the environment variable
|
||||
.BR LUA_INIT ,
|
||||
if it is defined.
|
||||
If the value of
|
||||
.B LUA_INIT
|
||||
is of the form
|
||||
.RI '@ filename ',
|
||||
then
|
||||
.I filename
|
||||
is executed.
|
||||
Otherwise, the string is assumed to be a Lua statement and is executed.
|
||||
.LP
|
||||
Options start with
|
||||
.B '\-'
|
||||
and are described below.
|
||||
You can use
|
||||
.B "'\--'"
|
||||
to signal the end of options.
|
||||
.LP
|
||||
If no arguments are given,
|
||||
then
|
||||
.B "\-v \-i"
|
||||
is assumed when the standard input is a terminal;
|
||||
otherwise,
|
||||
.B "\-"
|
||||
is assumed.
|
||||
.LP
|
||||
In interactive mode,
|
||||
.B lua
|
||||
prompts the user,
|
||||
reads lines from the standard input,
|
||||
and executes them as they are read.
|
||||
If a line does not contain a complete statement,
|
||||
then a secondary prompt is displayed and
|
||||
lines are read until a complete statement is formed or
|
||||
a syntax error is found.
|
||||
So, one way to interrupt the reading of an incomplete statement is
|
||||
to force a syntax error:
|
||||
adding a
|
||||
.B ';'
|
||||
in the middle of a statement is a sure way of forcing a syntax error
|
||||
(except inside multiline strings and comments; these must be closed explicitly).
|
||||
If a line starts with
|
||||
.BR '=' ,
|
||||
then
|
||||
.B lua
|
||||
displays the values of all the expressions in the remainder of the
|
||||
line. The expressions must be separated by commas.
|
||||
The primary prompt is the value of the global variable
|
||||
.BR _PROMPT ,
|
||||
if this value is a string;
|
||||
otherwise, the default prompt is used.
|
||||
Similarly, the secondary prompt is the value of the global variable
|
||||
.BR _PROMPT2 .
|
||||
So,
|
||||
to change the prompts,
|
||||
set the corresponding variable to a string of your choice.
|
||||
You can do that after calling the interpreter
|
||||
or on the command line
|
||||
(but in this case you have to be careful with quotes
|
||||
if the prompt string contains a space; otherwise you may confuse the shell.)
|
||||
The default prompts are "> " and ">> ".
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-
|
||||
load and execute the standard input as a file,
|
||||
that is,
|
||||
not interactively,
|
||||
even when the standard input is a terminal.
|
||||
.TP
|
||||
.BI \-e " stat"
|
||||
execute statement
|
||||
.IR stat .
|
||||
You need to quote
|
||||
.I stat
|
||||
if it contains spaces, quotes,
|
||||
or other characters special to the shell.
|
||||
.TP
|
||||
.B \-i
|
||||
enter interactive mode after
|
||||
.I script
|
||||
is executed.
|
||||
.TP
|
||||
.BI \-l " name"
|
||||
call
|
||||
.BI require(' name ')
|
||||
before executing
|
||||
.IR script .
|
||||
Typically used to load libraries.
|
||||
.TP
|
||||
.B \-v
|
||||
show version information.
|
||||
.SH "SEE ALSO"
|
||||
.BR luac (1)
|
||||
.br
|
||||
http://www.lua.org/
|
||||
.SH DIAGNOSTICS
|
||||
Error messages should be self explanatory.
|
||||
.SH AUTHORS
|
||||
R. Ierusalimschy,
|
||||
L. H. de Figueiredo,
|
||||
and
|
||||
W. Celes
|
||||
.\" EOF
|
||||
@ -1,136 +0,0 @@
|
||||
.\" $Id: luac.man,v 1.28 2006/01/06 16:03:34 lhf Exp $
|
||||
.TH LUAC 1 "$Date: 2006/01/06 16:03:34 $"
|
||||
.SH NAME
|
||||
luac \- Lua compiler
|
||||
.SH SYNOPSIS
|
||||
.B luac
|
||||
[
|
||||
.I options
|
||||
] [
|
||||
.I filenames
|
||||
]
|
||||
.SH DESCRIPTION
|
||||
.B luac
|
||||
is the Lua compiler.
|
||||
It translates programs written in the Lua programming language
|
||||
into binary files that can be later loaded and executed.
|
||||
.LP
|
||||
The main advantages of precompiling chunks are:
|
||||
faster loading,
|
||||
protecting source code from accidental user changes,
|
||||
and
|
||||
off-line syntax checking.
|
||||
.LP
|
||||
Pre-compiling does not imply faster execution
|
||||
because in Lua chunks are always compiled into bytecodes before being executed.
|
||||
.B luac
|
||||
simply allows those bytecodes to be saved in a file for later execution.
|
||||
.LP
|
||||
Pre-compiled chunks are not necessarily smaller than the corresponding source.
|
||||
The main goal in pre-compiling is faster loading.
|
||||
.LP
|
||||
The binary files created by
|
||||
.B luac
|
||||
are portable only among architectures with the same word size and byte order.
|
||||
.LP
|
||||
.B luac
|
||||
produces a single output file containing the bytecodes
|
||||
for all source files given.
|
||||
By default,
|
||||
the output file is named
|
||||
.BR luac.out ,
|
||||
but you can change this with the
|
||||
.B \-o
|
||||
option.
|
||||
.LP
|
||||
In the command line,
|
||||
you can mix
|
||||
text files containing Lua source and
|
||||
binary files containing precompiled chunks.
|
||||
This is useful to combine several precompiled chunks,
|
||||
even from different (but compatible) platforms,
|
||||
into a single precompiled chunk.
|
||||
.LP
|
||||
You can use
|
||||
.B "'\-'"
|
||||
to indicate the standard input as a source file
|
||||
and
|
||||
.B "'\--'"
|
||||
to signal the end of options
|
||||
(that is,
|
||||
all remaining arguments will be treated as files even if they start with
|
||||
.BR "'\-'" ).
|
||||
.LP
|
||||
The internal format of the binary files produced by
|
||||
.B luac
|
||||
is likely to change when a new version of Lua is released.
|
||||
So,
|
||||
save the source files of all Lua programs that you precompile.
|
||||
.LP
|
||||
.SH OPTIONS
|
||||
Options must be separate.
|
||||
.TP
|
||||
.B \-l
|
||||
produce a listing of the compiled bytecode for Lua's virtual machine.
|
||||
Listing bytecodes is useful to learn about Lua's virtual machine.
|
||||
If no files are given, then
|
||||
.B luac
|
||||
loads
|
||||
.B luac.out
|
||||
and lists its contents.
|
||||
.TP
|
||||
.BI \-o " file"
|
||||
output to
|
||||
.IR file ,
|
||||
instead of the default
|
||||
.BR luac.out .
|
||||
(You can use
|
||||
.B "'\-'"
|
||||
for standard output,
|
||||
but not on platforms that open standard output in text mode.)
|
||||
The output file may be a source file because
|
||||
all files are loaded before the output file is written.
|
||||
Be careful not to overwrite precious files.
|
||||
.TP
|
||||
.B \-p
|
||||
load files but do not generate any output file.
|
||||
Used mainly for syntax checking and for testing precompiled chunks:
|
||||
corrupted files will probably generate errors when loaded.
|
||||
Lua always performs a thorough integrity test on precompiled chunks.
|
||||
Bytecode that passes this test is completely safe,
|
||||
in the sense that it will not break the interpreter.
|
||||
However,
|
||||
there is no guarantee that such code does anything sensible.
|
||||
(None can be given, because the halting problem is unsolvable.)
|
||||
If no files are given, then
|
||||
.B luac
|
||||
loads
|
||||
.B luac.out
|
||||
and tests its contents.
|
||||
No messages are displayed if the file passes the integrity test.
|
||||
.TP
|
||||
.B \-s
|
||||
strip debug information before writing the output file.
|
||||
This saves some space in very large chunks,
|
||||
but if errors occur when running a stripped chunk,
|
||||
then the error messages may not contain the full information they usually do.
|
||||
For instance,
|
||||
line numbers and names of local variables are lost.
|
||||
.TP
|
||||
.B \-v
|
||||
show version information.
|
||||
.SH FILES
|
||||
.TP 15
|
||||
.B luac.out
|
||||
default output file
|
||||
.SH "SEE ALSO"
|
||||
.BR lua (1)
|
||||
.br
|
||||
http://www.lua.org/
|
||||
.SH DIAGNOSTICS
|
||||
Error messages should be self explanatory.
|
||||
.SH AUTHORS
|
||||
L. H. de Figueiredo,
|
||||
R. Ierusalimschy and
|
||||
W. Celes
|
||||
.\" EOF
|
||||
3
Utils/luadocumentor.bat
Normal file
3
Utils/luadocumentor.bat
Normal file
@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
"./luarocks/lua5.1" -e "package.path=\"./luarocks/systree/share/lua/5.1/?.lua;./luarocks/systree/share/lua/5.1/?/init.lua;./luarocks/systree/share/lua/5.1/?.lua;./luarocks/systree/share/lua/5.1/?/init.lua;./luarocks/lua/?.lua;\"..package.path; package.cpath=\"./luarocks/lib/lua/5.1/?.dll;./luarocks/systree/lib/lua/5.1/?.dll;\"..package.cpath" -e "local k,l,_=pcall(require,\"luarocks.loader\") _=k and l.add_context(\"luadocumentor\",\"0.1.5-1\")" "./luarocks/systree/lib/luarocks/rocks/luadocumentor/0.1.5-1/bin/luadocumentor" -f doc -d "../docs/Documentation" -s "../docs/Stylesheet/stylesheet.css" "../Moose Development/Moose" %*
|
||||
exit /b %ERRORLEVEL%
|
||||
8
Utils/luarocks/Microsoft.VC80.CRT.manifest
Normal file
8
Utils/luarocks/Microsoft.VC80.CRT.manifest
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<noInheritable></noInheritable>
|
||||
<assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
|
||||
<file name="msvcr80.dll" hash="10f4cb2831f1e9288a73387a8734a8b604e5beaa" hashalg="SHA1"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>n9On8FItNsK/DmT8UQxu6jYDtWQ=</dsig:DigestValue></asmv2:hash></file>
|
||||
<file name="msvcp80.dll" hash="b2082dfd3009365c5b287448dcb3b4e2158a6d26" hashalg="SHA1"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>0KJ/VTwP4OUHx98HlIW2AdW1kuY=</dsig:DigestValue></asmv2:hash></file>
|
||||
<file name="msvcm80.dll" hash="542490d0fcf8615c46d0ca487033ccaeb3941f0b" hashalg="SHA1"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>YJuB+9Os2oxW4mY+2oC/r8lICZE=</dsig:DigestValue></asmv2:hash></file>
|
||||
</assembly>
|
||||
BIN
Utils/luarocks/bin2c5.1.exe
Normal file
BIN
Utils/luarocks/bin2c5.1.exe
Normal file
Binary file not shown.
14
Utils/luarocks/config-5.1.lua
Normal file
14
Utils/luarocks/config-5.1.lua
Normal file
@ -0,0 +1,14 @@
|
||||
rocks_trees = {
|
||||
home..[[/luarocks]],
|
||||
{ name = [[user]],
|
||||
root = home..[[/luarocks]],
|
||||
},
|
||||
{ name = [[system]],
|
||||
root = [[C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\systree]],
|
||||
},
|
||||
}
|
||||
variables = {
|
||||
MSVCRT = 'MSVCR80',
|
||||
LUALIB = 'lua5.1.lib'
|
||||
}
|
||||
verbose = false -- set to 'true' to enable verbose output
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lua.h,v 1.218.1.7 2012/01/13 20:36:20 roberto Exp $
|
||||
** $Id: lua.h,v 1.218.1.5 2008/08/06 13:30:12 roberto Exp $
|
||||
** Lua - An Extensible Extension Language
|
||||
** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
|
||||
** See Copyright Notice at the end of this file
|
||||
@ -17,9 +17,9 @@
|
||||
|
||||
|
||||
#define LUA_VERSION "Lua 5.1"
|
||||
#define LUA_RELEASE "Lua 5.1.5"
|
||||
#define LUA_RELEASE "Lua 5.1.4"
|
||||
#define LUA_VERSION_NUM 501
|
||||
#define LUA_COPYRIGHT "Copyright (C) 1994-2012 Lua.org, PUC-Rio"
|
||||
#define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio"
|
||||
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes"
|
||||
|
||||
|
||||
@ -362,7 +362,7 @@ struct lua_Debug {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (C) 1994-2012 Lua.org, PUC-Rio. All rights reserved.
|
||||
* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@ -91,7 +91,7 @@
|
||||
".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \
|
||||
LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua"
|
||||
#define LUA_CPATH_DEFAULT \
|
||||
".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll"
|
||||
".\\?.dll;" ".\\?51.dll;" LUA_CDIR"?.dll;" LUA_CDIR"?51.dll;" LUA_CDIR"clibs\\?.dll;" LUA_CDIR"clibs\\?51.dll;" LUA_CDIR"loadall.dll;" LUA_CDIR"clibs\\loadall.dll"
|
||||
|
||||
#else
|
||||
#define LUA_ROOT "/usr/local/"
|
||||
@ -101,7 +101,7 @@
|
||||
"./?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \
|
||||
LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua"
|
||||
#define LUA_CPATH_DEFAULT \
|
||||
"./?.so;" LUA_CDIR"?.so;" LUA_CDIR"loadall.so"
|
||||
"./?.so;" "./lib?51.so;" LUA_CDIR"?.so;" LUA_CDIR"lib?51.so;" LUA_CDIR"loadall.so"
|
||||
#endif
|
||||
|
||||
|
||||
BIN
Utils/luarocks/lua.ico
Normal file
BIN
Utils/luarocks/lua.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
122
Utils/luarocks/lua/luarocks/add.lua
Normal file
122
Utils/luarocks/lua/luarocks/add.lua
Normal file
@ -0,0 +1,122 @@
|
||||
|
||||
--- Module implementing the luarocks-admin "add" command.
|
||||
-- Adds a rock or rockspec to a rocks server.
|
||||
local add = {}
|
||||
package.loaded["luarocks.add"] = add
|
||||
|
||||
local cfg = require("luarocks.cfg")
|
||||
local util = require("luarocks.util")
|
||||
local dir = require("luarocks.dir")
|
||||
local manif = require("luarocks.manif")
|
||||
local index = require("luarocks.index")
|
||||
local fs = require("luarocks.fs")
|
||||
local cache = require("luarocks.cache")
|
||||
|
||||
util.add_run_function(add)
|
||||
add.help_summary = "Add a rock or rockspec to a rocks server."
|
||||
add.help_arguments = "[--server=<server>] [--no-refresh] {<rockspec>|<rock>...}"
|
||||
add.help = [[
|
||||
Arguments are local files, which may be rockspecs or rocks.
|
||||
The flag --server indicates which server to use.
|
||||
If not given, the default server set in the upload_server variable
|
||||
from the configuration file is used instead.
|
||||
The flag --no-refresh indicates the local cache should not be refreshed
|
||||
prior to generation of the updated manifest.
|
||||
]]
|
||||
|
||||
local function add_files_to_server(refresh, rockfiles, server, upload_server)
|
||||
assert(type(refresh) == "boolean" or not refresh)
|
||||
assert(type(rockfiles) == "table")
|
||||
assert(type(server) == "string")
|
||||
assert(type(upload_server) == "table" or not upload_server)
|
||||
|
||||
local download_url, login_url = cache.get_server_urls(server, upload_server)
|
||||
local at = fs.current_dir()
|
||||
local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url
|
||||
|
||||
local local_cache, protocol, server_path, user, password = refresh_fn(server, download_url, cfg.upload_user, cfg.upload_password)
|
||||
if not local_cache then
|
||||
return nil, protocol
|
||||
end
|
||||
if protocol == "file" then
|
||||
return nil, "Server "..server.." is not recognized, check your configuration."
|
||||
end
|
||||
|
||||
if not login_url then
|
||||
login_url = protocol.."://"..server_path
|
||||
end
|
||||
|
||||
local ok, err = fs.change_dir(at)
|
||||
if not ok then return nil, err end
|
||||
|
||||
local files = {}
|
||||
for _, rockfile in ipairs(rockfiles) do
|
||||
if fs.exists(rockfile) then
|
||||
util.printout("Copying file "..rockfile.." to "..local_cache.."...")
|
||||
local absolute = fs.absolute_name(rockfile)
|
||||
fs.copy(absolute, local_cache, cfg.perm_read)
|
||||
table.insert(files, dir.base_name(absolute))
|
||||
else
|
||||
util.printerr("File "..rockfile.." not found")
|
||||
end
|
||||
end
|
||||
if #files == 0 then
|
||||
return nil, "No files found"
|
||||
end
|
||||
|
||||
local ok, err = fs.change_dir(local_cache)
|
||||
if not ok then return nil, err end
|
||||
|
||||
util.printout("Updating manifest...")
|
||||
manif.make_manifest(local_cache, "one", true)
|
||||
|
||||
manif.zip_manifests()
|
||||
|
||||
util.printout("Updating index.html...")
|
||||
index.make_index(local_cache)
|
||||
|
||||
local login_info = ""
|
||||
if user then login_info = " -u "..user end
|
||||
if password then login_info = login_info..":"..password end
|
||||
if not login_url:match("/$") then
|
||||
login_url = login_url .. "/"
|
||||
end
|
||||
|
||||
table.insert(files, "index.html")
|
||||
table.insert(files, "manifest")
|
||||
for ver in util.lua_versions() do
|
||||
table.insert(files, "manifest-"..ver)
|
||||
table.insert(files, "manifest-"..ver..".zip")
|
||||
end
|
||||
|
||||
-- TODO abstract away explicit 'curl' call
|
||||
|
||||
local cmd
|
||||
if protocol == "rsync" then
|
||||
local srv, path = server_path:match("([^/]+)(/.+)")
|
||||
cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/"
|
||||
elseif upload_server and upload_server.sftp then
|
||||
local part1, part2 = upload_server.sftp:match("^([^/]*)/(.*)$")
|
||||
cmd = cfg.variables.SCP.." "..table.concat(files, " ").." "..user.."@"..part1..":/"..part2
|
||||
else
|
||||
cmd = cfg.variables.CURL.." "..login_info.." -T '{"..table.concat(files, ",").."}' "..login_url
|
||||
end
|
||||
|
||||
util.printout(cmd)
|
||||
fs.execute(cmd)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function add.command(flags, ...)
|
||||
local files = {...}
|
||||
if #files < 1 then
|
||||
return nil, "Argument missing. "..util.see_help("add", "luarocks-admin")
|
||||
end
|
||||
local server, server_table = cache.get_upload_server(flags["server"])
|
||||
if not server then return nil, server_table end
|
||||
return add_files_to_server(not flags["no-refresh"], files, server, server_table)
|
||||
end
|
||||
|
||||
|
||||
return add
|
||||
92
Utils/luarocks/lua/luarocks/admin_remove.lua
Normal file
92
Utils/luarocks/lua/luarocks/admin_remove.lua
Normal file
@ -0,0 +1,92 @@
|
||||
|
||||
--- Module implementing the luarocks-admin "remove" command.
|
||||
-- Removes a rock or rockspec from a rocks server.
|
||||
local admin_remove = {}
|
||||
package.loaded["luarocks.admin_remove"] = admin_remove
|
||||
|
||||
local cfg = require("luarocks.cfg")
|
||||
local util = require("luarocks.util")
|
||||
local dir = require("luarocks.dir")
|
||||
local manif = require("luarocks.manif")
|
||||
local index = require("luarocks.index")
|
||||
local fs = require("luarocks.fs")
|
||||
local cache = require("luarocks.cache")
|
||||
|
||||
util.add_run_function(admin_remove)
|
||||
admin_remove.help_summary = "Remove a rock or rockspec from a rocks server."
|
||||
admin_remove.help_arguments = "[--server=<server>] [--no-refresh] {<rockspec>|<rock>...}"
|
||||
admin_remove.help = [[
|
||||
Arguments are local files, which may be rockspecs or rocks.
|
||||
The flag --server indicates which server to use.
|
||||
If not given, the default server set in the upload_server variable
|
||||
from the configuration file is used instead.
|
||||
The flag --no-refresh indicates the local cache should not be refreshed
|
||||
prior to generation of the updated manifest.
|
||||
]]
|
||||
|
||||
local function remove_files_from_server(refresh, rockfiles, server, upload_server)
|
||||
assert(type(refresh) == "boolean" or not refresh)
|
||||
assert(type(rockfiles) == "table")
|
||||
assert(type(server) == "string")
|
||||
assert(type(upload_server) == "table" or not upload_server)
|
||||
|
||||
local download_url, login_url = cache.get_server_urls(server, upload_server)
|
||||
local at = fs.current_dir()
|
||||
local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url
|
||||
|
||||
local local_cache, protocol, server_path, user, password = refresh_fn(server, download_url, cfg.upload_user, cfg.upload_password)
|
||||
if not local_cache then
|
||||
return nil, protocol
|
||||
end
|
||||
if protocol ~= "rsync" then
|
||||
return nil, "This command requires 'rsync', check your configuration."
|
||||
end
|
||||
|
||||
local ok, err = fs.change_dir(at)
|
||||
if not ok then return nil, err end
|
||||
|
||||
local nr_files = 0
|
||||
for _, rockfile in ipairs(rockfiles) do
|
||||
local basename = dir.base_name(rockfile)
|
||||
local file = dir.path(local_cache, basename)
|
||||
util.printout("Removing file "..file.."...")
|
||||
fs.delete(file)
|
||||
if not fs.exists(file) then
|
||||
nr_files = nr_files + 1
|
||||
else
|
||||
util.printerr("Failed removing "..file)
|
||||
end
|
||||
end
|
||||
if nr_files == 0 then
|
||||
return nil, "No files removed."
|
||||
end
|
||||
|
||||
local ok, err = fs.change_dir(local_cache)
|
||||
if not ok then return nil, err end
|
||||
|
||||
util.printout("Updating manifest...")
|
||||
manif.make_manifest(local_cache, "one", true)
|
||||
util.printout("Updating index.html...")
|
||||
index.make_index(local_cache)
|
||||
|
||||
local srv, path = server_path:match("([^/]+)(/.+)")
|
||||
local cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." --delete -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/"
|
||||
|
||||
util.printout(cmd)
|
||||
fs.execute(cmd)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function admin_remove.command(flags, ...)
|
||||
local files = {...}
|
||||
if #files < 1 then
|
||||
return nil, "Argument missing. "..util.see_help("remove", "luarocks-admin")
|
||||
end
|
||||
local server, server_table = cache.get_upload_server(flags["server"])
|
||||
if not server then return nil, server_table end
|
||||
return remove_files_from_server(not flags["no-refresh"], files, server, server_table)
|
||||
end
|
||||
|
||||
|
||||
return admin_remove
|
||||
415
Utils/luarocks/lua/luarocks/build.lua
Normal file
415
Utils/luarocks/lua/luarocks/build.lua
Normal file
@ -0,0 +1,415 @@
|
||||
|
||||
--- Module implementing the LuaRocks "build" command.
|
||||
-- Builds a rock, compiling its C parts if any.
|
||||
local build = {}
|
||||
package.loaded["luarocks.build"] = build
|
||||
|
||||
local pack = require("luarocks.pack")
|
||||
local path = require("luarocks.path")
|
||||
local util = require("luarocks.util")
|
||||
local repos = require("luarocks.repos")
|
||||
local fetch = require("luarocks.fetch")
|
||||
local fs = require("luarocks.fs")
|
||||
local dir = require("luarocks.dir")
|
||||
local deps = require("luarocks.deps")
|
||||
local manif = require("luarocks.manif")
|
||||
local remove = require("luarocks.remove")
|
||||
local cfg = require("luarocks.cfg")
|
||||
|
||||
util.add_run_function(build)
|
||||
build.help_summary = "Build/compile a rock."
|
||||
build.help_arguments = "[--pack-binary-rock] [--keep] {<rockspec>|<rock>|<name> [<version>]}"
|
||||
build.help = [[
|
||||
Build and install a rock, compiling its C parts if any.
|
||||
Argument may be a rockspec file, a source rock file
|
||||
or the name of a rock to be fetched from a repository.
|
||||
|
||||
--pack-binary-rock Do not install rock. Instead, produce a .rock file
|
||||
with the contents of compilation in the current
|
||||
directory.
|
||||
|
||||
--keep Do not remove previously installed versions of the
|
||||
rock after building a new one. This behavior can
|
||||
be made permanent by setting keep_other_versions=true
|
||||
in the configuration file.
|
||||
|
||||
--branch=<name> Override the `source.branch` field in the loaded
|
||||
rockspec. Allows to specify a different branch to
|
||||
fetch. Particularly for SCM rocks.
|
||||
|
||||
--only-deps Installs only the dependencies of the rock.
|
||||
|
||||
]]..util.deps_mode_help()
|
||||
|
||||
--- Install files to a given location.
|
||||
-- Takes a table where the array part is a list of filenames to be copied.
|
||||
-- In the hash part, other keys, if is_module_path is set, are identifiers
|
||||
-- in Lua module format, to indicate which subdirectory the file should be
|
||||
-- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo")
|
||||
-- will copy src/bar.lua to boo/foo.
|
||||
-- @param files table or nil: A table containing a list of files to copy in
|
||||
-- the format described above. If nil is passed, this function is a no-op.
|
||||
-- Directories should be delimited by forward slashes as in internet URLs.
|
||||
-- @param location string: The base directory files should be copied to.
|
||||
-- @param is_module_path boolean: True if string keys in files should be
|
||||
-- interpreted as dotted module paths.
|
||||
-- @param perms string: Permissions of the newly created files installed.
|
||||
-- Directories are always created with the default permissions.
|
||||
-- @return boolean or (nil, string): True if succeeded or
|
||||
-- nil and an error message.
|
||||
local function install_files(files, location, is_module_path, perms)
|
||||
assert(type(files) == "table" or not files)
|
||||
assert(type(location) == "string")
|
||||
if files then
|
||||
for k, file in pairs(files) do
|
||||
local dest = location
|
||||
local filename = dir.base_name(file)
|
||||
if type(k) == "string" then
|
||||
local modname = k
|
||||
if is_module_path then
|
||||
dest = dir.path(location, path.module_to_path(modname))
|
||||
local ok, err = fs.make_dir(dest)
|
||||
if not ok then return nil, err end
|
||||
if filename:match("%.lua$") then
|
||||
local basename = modname:match("([^.]+)$")
|
||||
filename = basename..".lua"
|
||||
end
|
||||
else
|
||||
dest = dir.path(location, dir.dir_name(modname))
|
||||
local ok, err = fs.make_dir(dest)
|
||||
if not ok then return nil, err end
|
||||
filename = dir.base_name(modname)
|
||||
end
|
||||
else
|
||||
local ok, err = fs.make_dir(dest)
|
||||
if not ok then return nil, err end
|
||||
end
|
||||
local ok = fs.copy(dir.path(file), dir.path(dest, filename), perms)
|
||||
if not ok then
|
||||
return nil, "Failed copying "..file
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Write to the current directory the contents of a table,
|
||||
-- where each key is a file name and its value is the file content.
|
||||
-- @param files table: The table of files to be written.
|
||||
local function extract_from_rockspec(files)
|
||||
for name, content in pairs(files) do
|
||||
local fd = io.open(dir.path(fs.current_dir(), name), "w+")
|
||||
fd:write(content)
|
||||
fd:close()
|
||||
end
|
||||
end
|
||||
|
||||
--- Applies patches inlined in the build.patches section
|
||||
-- and extracts files inlined in the build.extra_files section
|
||||
-- of a rockspec.
|
||||
-- @param rockspec table: A rockspec table.
|
||||
-- @return boolean or (nil, string): True if succeeded or
|
||||
-- nil and an error message.
|
||||
function build.apply_patches(rockspec)
|
||||
assert(type(rockspec) == "table")
|
||||
|
||||
local build_spec = rockspec.build
|
||||
if build_spec.extra_files then
|
||||
extract_from_rockspec(build_spec.extra_files)
|
||||
end
|
||||
if build_spec.patches then
|
||||
extract_from_rockspec(build_spec.patches)
|
||||
for patch, patchdata in util.sortedpairs(build_spec.patches) do
|
||||
util.printout("Applying patch "..patch.."...")
|
||||
local ok, err = fs.apply_patch(tostring(patch), patchdata)
|
||||
if not ok then
|
||||
return nil, "Failed applying patch "..patch
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function install_default_docs(name, version)
|
||||
local patterns = { "readme", "license", "copying", ".*%.md" }
|
||||
local dest = dir.path(path.install_dir(name, version), "doc")
|
||||
local has_dir = false
|
||||
for file in fs.dir() do
|
||||
for _, pattern in ipairs(patterns) do
|
||||
if file:lower():match("^"..pattern) then
|
||||
if not has_dir then
|
||||
fs.make_dir(dest)
|
||||
has_dir = true
|
||||
end
|
||||
fs.copy(file, dest, cfg.perm_read)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Build and install a rock given a rockspec.
|
||||
-- @param rockspec_file string: local or remote filename of a rockspec.
|
||||
-- @param need_to_fetch boolean: true if sources need to be fetched,
|
||||
-- false if the rockspec was obtained from inside a source rock.
|
||||
-- @param minimal_mode boolean: true if there's no need to fetch,
|
||||
-- unpack or change dir (this is used by "luarocks make"). Implies
|
||||
-- need_to_fetch = false.
|
||||
-- @param deps_mode string: Dependency mode: "one" for the current default tree,
|
||||
-- "all" for all trees, "order" for all trees with priority >= the current default,
|
||||
-- "none" for no trees.
|
||||
-- @param build_only_deps boolean: true to build the listed dependencies only.
|
||||
-- @return (string, string) or (nil, string, [string]): Name and version of
|
||||
-- installed rock if succeeded or nil and an error message followed by an error code.
|
||||
function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode, build_only_deps)
|
||||
assert(type(rockspec_file) == "string")
|
||||
assert(type(need_to_fetch) == "boolean")
|
||||
|
||||
local rockspec, err, errcode = fetch.load_rockspec(rockspec_file)
|
||||
if err then
|
||||
return nil, err, errcode
|
||||
elseif not rockspec.build then
|
||||
return nil, "Rockspec error: build table not specified"
|
||||
elseif not rockspec.build.type then
|
||||
return nil, "Rockspec error: build type not specified"
|
||||
end
|
||||
|
||||
local ok
|
||||
if not build_only_deps then
|
||||
ok, err, errcode = deps.check_external_deps(rockspec, "build")
|
||||
if err then
|
||||
return nil, err, errcode
|
||||
end
|
||||
end
|
||||
|
||||
if deps_mode == "none" then
|
||||
util.printerr("Warning: skipping dependency checks.")
|
||||
else
|
||||
local ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode)
|
||||
if err then
|
||||
return nil, err, errcode
|
||||
end
|
||||
end
|
||||
|
||||
local name, version = rockspec.name, rockspec.version
|
||||
if build_only_deps then
|
||||
util.printout("Stopping after installing dependencies for " ..name.." "..version)
|
||||
util.printout()
|
||||
return name, version
|
||||
end
|
||||
|
||||
if repos.is_installed(name, version) then
|
||||
repos.delete_version(name, version, deps_mode)
|
||||
end
|
||||
|
||||
if not minimal_mode then
|
||||
local source_dir
|
||||
if need_to_fetch then
|
||||
ok, source_dir, errcode = fetch.fetch_sources(rockspec, true)
|
||||
if not ok then
|
||||
return nil, source_dir, errcode
|
||||
end
|
||||
local ok, err = fs.change_dir(source_dir)
|
||||
if not ok then return nil, err end
|
||||
elseif rockspec.source.file then
|
||||
local ok, err = fs.unpack_archive(rockspec.source.file)
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
fs.change_dir(rockspec.source.dir)
|
||||
end
|
||||
|
||||
local dirs = {
|
||||
lua = { name = path.lua_dir(name, version), is_module_path = true, perms = cfg.perm_read },
|
||||
lib = { name = path.lib_dir(name, version), is_module_path = true, perms = cfg.perm_exec },
|
||||
conf = { name = path.conf_dir(name, version), is_module_path = false, perms = cfg.perm_read },
|
||||
bin = { name = path.bin_dir(name, version), is_module_path = false, perms = cfg.perm_exec },
|
||||
}
|
||||
|
||||
for _, d in pairs(dirs) do
|
||||
local ok, err = fs.make_dir(d.name)
|
||||
if not ok then return nil, err end
|
||||
end
|
||||
local rollback = util.schedule_function(function()
|
||||
fs.delete(path.install_dir(name, version))
|
||||
fs.remove_dir_if_empty(path.versions_dir(name))
|
||||
end)
|
||||
|
||||
local build_spec = rockspec.build
|
||||
|
||||
if not minimal_mode then
|
||||
ok, err = build.apply_patches(rockspec)
|
||||
if err then
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
|
||||
if build_spec.type ~= "none" then
|
||||
|
||||
-- Temporary compatibility
|
||||
if build_spec.type == "module" then
|
||||
util.printout("Do not use 'module' as a build type. Use 'builtin' instead.")
|
||||
build_spec.type = "builtin"
|
||||
end
|
||||
|
||||
if cfg.accepted_build_types and util.array_contains(cfg.accepted_build_types, build_spec.type) then
|
||||
return nil, "This rockspec uses the '"..build_spec.type.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration."
|
||||
end
|
||||
|
||||
local build_type
|
||||
ok, build_type = pcall(require, "luarocks.build." .. build_spec.type)
|
||||
if not ok or not type(build_type) == "table" then
|
||||
return nil, "Failed initializing build back-end for build type '"..build_spec.type.."': "..build_type
|
||||
end
|
||||
|
||||
ok, err = build_type.run(rockspec)
|
||||
if not ok then
|
||||
return nil, "Build error: " .. err
|
||||
end
|
||||
end
|
||||
|
||||
if build_spec.install then
|
||||
for id, install_dir in pairs(dirs) do
|
||||
ok, err = install_files(build_spec.install[id], install_dir.name, install_dir.is_module_path, install_dir.perms)
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local copy_directories = build_spec.copy_directories
|
||||
local copying_default = false
|
||||
if not copy_directories then
|
||||
copy_directories = {"doc"}
|
||||
copying_default = true
|
||||
end
|
||||
|
||||
local any_docs = false
|
||||
for _, copy_dir in pairs(copy_directories) do
|
||||
if fs.is_dir(copy_dir) then
|
||||
local dest = dir.path(path.install_dir(name, version), copy_dir)
|
||||
fs.make_dir(dest)
|
||||
fs.copy_contents(copy_dir, dest)
|
||||
any_docs = true
|
||||
else
|
||||
if not copying_default then
|
||||
return nil, "Directory '"..copy_dir.."' not found"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not any_docs then
|
||||
install_default_docs(name, version)
|
||||
end
|
||||
|
||||
for _, d in pairs(dirs) do
|
||||
fs.remove_dir_if_empty(d.name)
|
||||
end
|
||||
|
||||
fs.pop_dir()
|
||||
|
||||
fs.copy(rockspec.local_filename, path.rockspec_file(name, version), cfg.perm_read)
|
||||
if need_to_fetch then
|
||||
fs.pop_dir()
|
||||
end
|
||||
|
||||
ok, err = manif.make_rock_manifest(name, version)
|
||||
if err then return nil, err end
|
||||
|
||||
ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode)
|
||||
if err then return nil, err end
|
||||
|
||||
util.remove_scheduled_function(rollback)
|
||||
rollback = util.schedule_function(function()
|
||||
repos.delete_version(name, version, deps_mode)
|
||||
end)
|
||||
|
||||
ok, err = repos.run_hook(rockspec, "post_install")
|
||||
if err then return nil, err end
|
||||
|
||||
util.announce_install(rockspec)
|
||||
util.remove_scheduled_function(rollback)
|
||||
return name, version
|
||||
end
|
||||
|
||||
--- Build and install a rock.
|
||||
-- @param rock_file string: local or remote filename of a rock.
|
||||
-- @param need_to_fetch boolean: true if sources need to be fetched,
|
||||
-- false if the rockspec was obtained from inside a source rock.
|
||||
-- @param deps_mode: string: Which trees to check dependencies for:
|
||||
-- "one" for the current default tree, "all" for all trees,
|
||||
-- "order" for all trees with priority >= the current default, "none" for no trees.
|
||||
-- @param build_only_deps boolean: true to build the listed dependencies only.
|
||||
-- @return boolean or (nil, string, [string]): True if build was successful,
|
||||
-- or false and an error message and an optional error code.
|
||||
function build.build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps)
|
||||
assert(type(rock_file) == "string")
|
||||
assert(type(need_to_fetch) == "boolean")
|
||||
|
||||
local ok, err, errcode
|
||||
local unpack_dir
|
||||
unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file)
|
||||
if not unpack_dir then
|
||||
return nil, err, errcode
|
||||
end
|
||||
local rockspec_file = path.rockspec_name_from_rock(rock_file)
|
||||
ok, err = fs.change_dir(unpack_dir)
|
||||
if not ok then return nil, err end
|
||||
ok, err, errcode = build.build_rockspec(rockspec_file, need_to_fetch, false, deps_mode, build_only_deps)
|
||||
fs.pop_dir()
|
||||
return ok, err, errcode
|
||||
end
|
||||
|
||||
local function do_build(name, version, deps_mode, build_only_deps)
|
||||
if name:match("%.rockspec$") then
|
||||
return build.build_rockspec(name, true, false, deps_mode, build_only_deps)
|
||||
elseif name:match("%.src%.rock$") then
|
||||
return build.build_rock(name, false, deps_mode, build_only_deps)
|
||||
elseif name:match("%.all%.rock$") then
|
||||
local install = require("luarocks.install")
|
||||
local install_fun = build_only_deps and install.install_binary_rock_deps or install.install_binary_rock
|
||||
return install_fun(name, deps_mode)
|
||||
elseif name:match("%.rock$") then
|
||||
return build.build_rock(name, true, deps_mode, build_only_deps)
|
||||
elseif not name:match(dir.separator) then
|
||||
local search = require("luarocks.search")
|
||||
return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps)
|
||||
end
|
||||
return nil, "Don't know what to do with "..name
|
||||
end
|
||||
|
||||
--- Driver function for "build" command.
|
||||
-- @param name string: A local or remote rockspec or rock file.
|
||||
-- If a package name is given, forwards the request to "search" and,
|
||||
-- if returned a result, installs the matching rock.
|
||||
-- @param version string: When passing a package name, a version number may
|
||||
-- also be given.
|
||||
-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an
|
||||
-- error message otherwise. exitcode is optionally returned.
|
||||
function build.command(flags, name, version)
|
||||
if type(name) ~= "string" then
|
||||
return nil, "Argument missing. "..util.see_help("build")
|
||||
end
|
||||
assert(type(version) == "string" or not version)
|
||||
|
||||
if flags["pack-binary-rock"] then
|
||||
return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags))
|
||||
else
|
||||
local ok, err = fs.check_command_permissions(flags)
|
||||
if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
|
||||
ok, err = do_build(name, version, deps.get_deps_mode(flags), flags["only-deps"])
|
||||
if not ok then return nil, err end
|
||||
name, version = ok, err
|
||||
|
||||
if (not flags["only-deps"]) and (not flags["keep"]) and not cfg.keep_other_versions then
|
||||
local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"])
|
||||
if not ok then util.printerr(err) end
|
||||
end
|
||||
|
||||
manif.check_dependencies(nil, deps.get_deps_mode(flags))
|
||||
return name, version
|
||||
end
|
||||
end
|
||||
|
||||
return build
|
||||
78
Utils/luarocks/lua/luarocks/cache.lua
Normal file
78
Utils/luarocks/lua/luarocks/cache.lua
Normal file
@ -0,0 +1,78 @@
|
||||
|
||||
--- Module handling the LuaRocks local cache.
|
||||
-- Adds a rock or rockspec to a rocks server.
|
||||
local cache = {}
|
||||
package.loaded["luarocks.cache"] = cache
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
local cfg = require("luarocks.cfg")
|
||||
local dir = require("luarocks.dir")
|
||||
local util = require("luarocks.util")
|
||||
|
||||
function cache.get_upload_server(server)
|
||||
if not server then server = cfg.upload_server end
|
||||
if not server then
|
||||
return nil, "No server specified and no default configured with upload_server."
|
||||
end
|
||||
return server, cfg.upload_servers and cfg.upload_servers[server]
|
||||
end
|
||||
|
||||
function cache.get_server_urls(server, upload_server)
|
||||
local download_url = server
|
||||
local login_url = nil
|
||||
if upload_server then
|
||||
if upload_server.rsync then download_url = "rsync://"..upload_server.rsync
|
||||
elseif upload_server.http then download_url = "http://"..upload_server.http
|
||||
elseif upload_server.ftp then download_url = "ftp://"..upload_server.ftp
|
||||
end
|
||||
|
||||
if upload_server.ftp then login_url = "ftp://"..upload_server.ftp
|
||||
elseif upload_server.sftp then login_url = "sftp://"..upload_server.sftp
|
||||
end
|
||||
end
|
||||
return download_url, login_url
|
||||
end
|
||||
|
||||
function cache.split_server_url(server, url, user, password)
|
||||
local protocol, server_path = dir.split_url(url)
|
||||
if server_path:match("@") then
|
||||
local credentials
|
||||
credentials, server_path = server_path:match("([^@]*)@(.*)")
|
||||
if credentials:match(":") then
|
||||
user, password = credentials:match("([^:]*):(.*)")
|
||||
else
|
||||
user = credentials
|
||||
end
|
||||
end
|
||||
local local_cache = cfg.local_cache .. "/" .. server
|
||||
return local_cache, protocol, server_path, user, password
|
||||
end
|
||||
|
||||
function cache.refresh_local_cache(server, url, user, password)
|
||||
local local_cache, protocol, server_path, user, password = cache.split_server_url(server, url, user, password)
|
||||
local ok, err = fs.make_dir(local_cache)
|
||||
if not ok then
|
||||
return nil, "Failed creating local cache dir: "..err
|
||||
end
|
||||
fs.change_dir(local_cache)
|
||||
if not ok then return nil, err end
|
||||
util.printout("Refreshing cache "..local_cache.."...")
|
||||
|
||||
-- TODO abstract away explicit 'wget' call
|
||||
local ok = false
|
||||
if protocol == "rsync" then
|
||||
local srv, path = server_path:match("([^/]+)(/.+)")
|
||||
ok = fs.execute(cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." -e ssh "..user.."@"..srv..":"..path.."/ "..local_cache.."/")
|
||||
else
|
||||
local login_info = ""
|
||||
if user then login_info = " --user="..user end
|
||||
if password then login_info = login_info .. " --password="..password end
|
||||
ok = fs.execute(cfg.variables.WGET.." --no-cache -q -m -np -nd "..protocol.."://"..server_path..login_info)
|
||||
end
|
||||
if not ok then
|
||||
return nil, "Failed downloading cache."
|
||||
end
|
||||
return local_cache, protocol, server_path, user, password
|
||||
end
|
||||
|
||||
return cache
|
||||
760
Utils/luarocks/lua/luarocks/cfg.lua
Normal file
760
Utils/luarocks/lua/luarocks/cfg.lua
Normal file
@ -0,0 +1,760 @@
|
||||
--- Configuration for LuaRocks.
|
||||
-- Tries to load the user's configuration file and
|
||||
-- defines defaults for unset values. See the
|
||||
-- <a href="http://luarocks.org/en/Config_file_format">config
|
||||
-- file format documentation</a> for details.
|
||||
--
|
||||
-- End-users shouldn't edit this file. They can override any defaults
|
||||
-- set in this file using their system-wide or user-specific configuration
|
||||
-- files. Run `luarocks` with no arguments to see the locations of
|
||||
-- these files in your platform.
|
||||
|
||||
local rawset, next, table, pairs, require, io, os, setmetatable, pcall, ipairs, package, tonumber, type, assert, _VERSION =
|
||||
rawset, next, table, pairs, require, io, os, setmetatable, pcall, ipairs, package, tonumber, type, assert, _VERSION
|
||||
|
||||
--module("luarocks.cfg")
|
||||
local cfg = {}
|
||||
package.loaded["luarocks.cfg"] = cfg
|
||||
|
||||
local util = require("luarocks.util")
|
||||
|
||||
cfg.lua_version = _VERSION:match(" (5%.[123])$") or "5.1"
|
||||
local version_suffix = cfg.lua_version:gsub("%.", "_")
|
||||
|
||||
-- Load site-local global configurations
|
||||
local ok, site_config = pcall(require, "luarocks.site_config_"..version_suffix)
|
||||
if not ok then
|
||||
ok, site_config = pcall(require, "luarocks.site_config")
|
||||
end
|
||||
if not ok then
|
||||
io.stderr:write("Site-local luarocks/site_config.lua file not found. Incomplete installation?\n")
|
||||
site_config = {}
|
||||
end
|
||||
|
||||
cfg.program_version = "2.4.2"
|
||||
cfg.program_series = "2.4"
|
||||
cfg.major_version = (cfg.program_version:match("([^.]%.[^.])")) or cfg.program_series
|
||||
cfg.variables = {}
|
||||
cfg.rocks_trees = {}
|
||||
cfg.platforms = {}
|
||||
|
||||
local persist = require("luarocks.persist")
|
||||
|
||||
cfg.errorcodes = setmetatable({
|
||||
OK = 0,
|
||||
UNSPECIFIED = 1,
|
||||
PERMISSIONDENIED = 2,
|
||||
CONFIGFILE = 3,
|
||||
CRASH = 99
|
||||
},{
|
||||
__index = function(t, key)
|
||||
local val = rawget(t, key)
|
||||
if not val then
|
||||
error("'"..tostring(key).."' is not a valid errorcode", 2)
|
||||
end
|
||||
return val
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
local popen_ok, popen_result = pcall(io.popen, "")
|
||||
if popen_ok then
|
||||
if popen_result then
|
||||
popen_result:close()
|
||||
end
|
||||
else
|
||||
io.stderr:write("Your version of Lua does not support io.popen,\n")
|
||||
io.stderr:write("which is required by LuaRocks. Please check your Lua installation.\n")
|
||||
os.exit(cfg.errorcodes.UNSPECIFIED)
|
||||
end
|
||||
|
||||
-- System detection:
|
||||
|
||||
-- A proper installation of LuaRocks will hardcode the system
|
||||
-- and proc values with site_config.LUAROCKS_UNAME_S and site_config.LUAROCKS_UNAME_M,
|
||||
-- so that this detection does not run every time. When it is
|
||||
-- performed, we use the Unix way to identify the system,
|
||||
-- even on Windows (assuming UnxUtils or Cygwin).
|
||||
local system = site_config.LUAROCKS_UNAME_S or io.popen("uname -s"):read("*l")
|
||||
local proc = site_config.LUAROCKS_UNAME_M or io.popen("uname -m"):read("*l")
|
||||
if proc:match("i[%d]86") then
|
||||
cfg.target_cpu = "x86"
|
||||
elseif proc:match("amd64") or proc:match("x86_64") then
|
||||
cfg.target_cpu = "x86_64"
|
||||
elseif proc:match("Power Macintosh") then
|
||||
cfg.target_cpu = "powerpc"
|
||||
else
|
||||
cfg.target_cpu = proc
|
||||
end
|
||||
|
||||
if system == "FreeBSD" then
|
||||
cfg.platforms.unix = true
|
||||
cfg.platforms.freebsd = true
|
||||
cfg.platforms.bsd = true
|
||||
elseif system == "OpenBSD" then
|
||||
cfg.platforms.unix = true
|
||||
cfg.platforms.openbsd = true
|
||||
cfg.platforms.bsd = true
|
||||
elseif system == "NetBSD" then
|
||||
cfg.platforms.unix = true
|
||||
cfg.platforms.netbsd = true
|
||||
cfg.platforms.bsd = true
|
||||
elseif system == "Darwin" then
|
||||
cfg.platforms.unix = true
|
||||
cfg.platforms.macosx = true
|
||||
cfg.platforms.bsd = true
|
||||
elseif system == "Linux" then
|
||||
cfg.platforms.unix = true
|
||||
cfg.platforms.linux = true
|
||||
elseif system == "SunOS" then
|
||||
cfg.platforms.unix = true
|
||||
cfg.platforms.solaris = true
|
||||
elseif system and system:match("^CYGWIN") then
|
||||
cfg.platforms.unix = true
|
||||
cfg.platforms.cygwin = true
|
||||
elseif system and system:match("^MSYS") then
|
||||
cfg.platforms.unix = true
|
||||
cfg.platforms.msys = true
|
||||
cfg.platforms.cygwin = true
|
||||
elseif system and system:match("^Windows") then
|
||||
cfg.platforms.windows = true
|
||||
cfg.platforms.win32 = true
|
||||
elseif system and system:match("^MINGW") then
|
||||
cfg.platforms.windows = true
|
||||
cfg.platforms.mingw32 = true
|
||||
cfg.platforms.win32 = true
|
||||
elseif system == "Haiku" then
|
||||
cfg.platforms.unix = true
|
||||
cfg.platforms.haiku = true
|
||||
else
|
||||
cfg.platforms.unix = true
|
||||
-- Fall back to Unix in unknown systems.
|
||||
end
|
||||
|
||||
-- Set order for platform overrides.
|
||||
-- More general platform identifiers should be listed first,
|
||||
-- more specific ones later.
|
||||
local platform_order = {
|
||||
-- Unixes
|
||||
"unix",
|
||||
"bsd",
|
||||
"solaris",
|
||||
"netbsd",
|
||||
"openbsd",
|
||||
"freebsd",
|
||||
"linux",
|
||||
"macosx",
|
||||
"cygwin",
|
||||
"msys",
|
||||
"haiku",
|
||||
-- Windows
|
||||
"win32",
|
||||
"mingw32",
|
||||
"windows",
|
||||
}
|
||||
|
||||
-- Path configuration:
|
||||
local sys_config_file, home_config_file
|
||||
local sys_config_file_default, home_config_file_default
|
||||
local sys_config_dir, home_config_dir
|
||||
local sys_config_ok, home_config_ok = false, false
|
||||
local extra_luarocks_module_dir
|
||||
sys_config_dir = site_config.LUAROCKS_SYSCONFDIR or site_config.LUAROCKS_PREFIX
|
||||
if cfg.platforms.windows then
|
||||
cfg.home = os.getenv("APPDATA") or "c:"
|
||||
sys_config_dir = sys_config_dir or "c:/luarocks"
|
||||
home_config_dir = cfg.home.."/luarocks"
|
||||
cfg.home_tree = cfg.home.."/luarocks/"
|
||||
else
|
||||
cfg.home = os.getenv("HOME") or ""
|
||||
sys_config_dir = sys_config_dir or "/etc/luarocks"
|
||||
home_config_dir = cfg.home.."/.luarocks"
|
||||
cfg.home_tree = (os.getenv("USER") ~= "root") and cfg.home.."/.luarocks/"
|
||||
end
|
||||
|
||||
-- Create global environment for the config files;
|
||||
local env_for_config_file = function()
|
||||
local e
|
||||
e = {
|
||||
home = cfg.home,
|
||||
lua_version = cfg.lua_version,
|
||||
platforms = util.make_shallow_copy(cfg.platforms),
|
||||
processor = cfg.target_cpu, -- remains for compat reasons
|
||||
target_cpu = cfg.target_cpu, -- replaces `processor`
|
||||
os_getenv = os.getenv,
|
||||
dump_env = function()
|
||||
-- debug function, calling it from a config file will show all
|
||||
-- available globals to that config file
|
||||
print(util.show_table(e, "global environment"))
|
||||
end,
|
||||
}
|
||||
return e
|
||||
end
|
||||
|
||||
-- Merge values from config files read into the `cfg` table
|
||||
local merge_overrides = function(overrides)
|
||||
-- remove some stuff we do not want to integrate
|
||||
overrides.os_getenv = nil
|
||||
overrides.dump_env = nil
|
||||
-- remove tables to be copied verbatim instead of deeply merged
|
||||
if overrides.rocks_trees then cfg.rocks_trees = nil end
|
||||
if overrides.rocks_servers then cfg.rocks_servers = nil end
|
||||
-- perform actual merge
|
||||
util.deep_merge(cfg, overrides)
|
||||
end
|
||||
|
||||
-- load config file from a list until first succesful one. Info is
|
||||
-- added to `cfg` module table, returns filepath of succesfully loaded
|
||||
-- file or nil if it failed
|
||||
local load_config_file = function(list)
|
||||
for _, filepath in ipairs(list) do
|
||||
local result, err, errcode = persist.load_into_table(filepath, env_for_config_file())
|
||||
if (not result) and errcode ~= "open" then
|
||||
-- errcode is either "load" or "run"; bad config file, so error out
|
||||
io.stderr:write(err.."\n")
|
||||
os.exit(cfg.errorcodes.CONFIGFILE)
|
||||
end
|
||||
if result then
|
||||
-- succes in loading and running, merge contents and exit
|
||||
merge_overrides(result)
|
||||
return filepath
|
||||
end
|
||||
end
|
||||
return nil -- nothing was loaded
|
||||
end
|
||||
|
||||
|
||||
-- Load system configuration file
|
||||
do
|
||||
sys_config_file_default = sys_config_dir.."/config-"..cfg.lua_version..".lua"
|
||||
sys_config_file = load_config_file({
|
||||
site_config.LUAROCKS_SYSCONFIG or sys_config_file_default,
|
||||
sys_config_dir.."/config.lua",
|
||||
})
|
||||
sys_config_ok = (sys_config_file ~= nil)
|
||||
end
|
||||
|
||||
-- Load user configuration file (if allowed)
|
||||
if not site_config.LUAROCKS_FORCE_CONFIG then
|
||||
|
||||
home_config_file_default = home_config_dir.."/config-"..cfg.lua_version..".lua"
|
||||
|
||||
local config_env_var = "LUAROCKS_CONFIG_" .. version_suffix
|
||||
local config_env_value = os.getenv(config_env_var)
|
||||
if not config_env_value then
|
||||
config_env_var = "LUAROCKS_CONFIG"
|
||||
config_env_value = os.getenv(config_env_var)
|
||||
end
|
||||
|
||||
-- first try environment provided file, so we can explicitly warn when it is missing
|
||||
if config_env_value then
|
||||
local list = { config_env_value }
|
||||
home_config_file = load_config_file(list)
|
||||
home_config_ok = (home_config_file ~= nil)
|
||||
if not home_config_ok then
|
||||
io.stderr:write("Warning: could not load configuration file `"..config_env_value.."` given in environment variable "..config_env_var.."\n")
|
||||
end
|
||||
end
|
||||
|
||||
-- try the alternative defaults if there was no environment specified file or it didn't work
|
||||
if not home_config_ok then
|
||||
local list = {
|
||||
home_config_file_default,
|
||||
home_config_dir.."/config.lua",
|
||||
}
|
||||
home_config_file = load_config_file(list)
|
||||
home_config_ok = (home_config_file ~= nil)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if not next(cfg.rocks_trees) then
|
||||
if cfg.home_tree then
|
||||
table.insert(cfg.rocks_trees, { name = "user", root = cfg.home_tree } )
|
||||
end
|
||||
if site_config.LUAROCKS_ROCKS_TREE then
|
||||
table.insert(cfg.rocks_trees, { name = "system", root = site_config.LUAROCKS_ROCKS_TREE } )
|
||||
end
|
||||
end
|
||||
|
||||
-- update platforms list; keyed -> array
|
||||
do
|
||||
-- if explicitly given by user,
|
||||
if cfg.platforms[1] then
|
||||
local is_windows = cfg.platforms.windows
|
||||
-- Clear auto-detected values
|
||||
for k, _ in pairs(cfg.platforms) do
|
||||
if type(k) == "string" then
|
||||
cfg.platforms[k] = nil
|
||||
end
|
||||
end
|
||||
-- and set the ones given by the user.
|
||||
for _, plat in ipairs(cfg.platforms) do
|
||||
cfg.platforms[plat] = true
|
||||
end
|
||||
-- If no major platform family was set by the user,
|
||||
if not (cfg.platforms.unix or cfg.platforms.windows) then
|
||||
-- set some fallback defaults in case the user provides an incomplete configuration.
|
||||
-- LuaRocks expects a set of defaults to be available.
|
||||
-- This is used for setting defaults here only; the platform overrides
|
||||
-- will use only the user's list.
|
||||
if is_windows then
|
||||
cfg.platforms.windows = true
|
||||
table.insert(cfg.platforms, "windows")
|
||||
else
|
||||
cfg.platforms.unix = true
|
||||
table.insert(cfg.platforms, "unix")
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Sort detected platform defaults
|
||||
local order = {}
|
||||
for i, v in ipairs(platform_order) do
|
||||
order[v] = i
|
||||
end
|
||||
local entries = {}
|
||||
for k, v in pairs(cfg.platforms) do
|
||||
if type(k) == "string" and v == true then
|
||||
table.insert(entries, k)
|
||||
end
|
||||
end
|
||||
table.sort(entries, function(a, b) return order[a] < order[b] end)
|
||||
util.deep_merge(cfg.platforms, entries)
|
||||
end
|
||||
end
|
||||
|
||||
-- Configure defaults:
|
||||
local defaults = {
|
||||
|
||||
local_by_default = false,
|
||||
accept_unknown_fields = false,
|
||||
fs_use_modules = true,
|
||||
hooks_enabled = true,
|
||||
deps_mode = "one",
|
||||
check_certificates = false,
|
||||
perm_read = "0644",
|
||||
perm_exec = "0755",
|
||||
|
||||
lua_modules_path = "/share/lua/"..cfg.lua_version,
|
||||
lib_modules_path = "/lib/lua/"..cfg.lua_version,
|
||||
rocks_subdir = site_config.LUAROCKS_ROCKS_SUBDIR or "/lib/luarocks/rocks",
|
||||
|
||||
arch = "unknown",
|
||||
lib_extension = "unknown",
|
||||
obj_extension = "unknown",
|
||||
link_lua_explicitly = false,
|
||||
|
||||
rocks_servers = {
|
||||
{
|
||||
"https://luarocks.org",
|
||||
"https://raw.githubusercontent.com/rocks-moonscript-org/moonrocks-mirror/master/",
|
||||
"http://luafr.org/moonrocks/",
|
||||
"http://luarocks.logiceditor.com/rocks",
|
||||
}
|
||||
},
|
||||
disabled_servers = {},
|
||||
|
||||
upload = {
|
||||
server = "https://luarocks.org",
|
||||
tool_version = "1.0.0",
|
||||
api_version = "1",
|
||||
},
|
||||
|
||||
lua_extension = "lua",
|
||||
lua_interpreter = site_config.LUA_INTERPRETER or "lua",
|
||||
downloader = site_config.LUAROCKS_DOWNLOADER or "wget",
|
||||
md5checker = site_config.LUAROCKS_MD5CHECKER or "md5sum",
|
||||
connection_timeout = 30, -- 0 = no timeout
|
||||
|
||||
variables = {
|
||||
MAKE = "make",
|
||||
CC = "cc",
|
||||
LD = "ld",
|
||||
|
||||
CVS = "cvs",
|
||||
GIT = "git",
|
||||
SSCM = "sscm",
|
||||
SVN = "svn",
|
||||
HG = "hg",
|
||||
|
||||
RSYNC = "rsync",
|
||||
WGET = "wget",
|
||||
SCP = "scp",
|
||||
CURL = "curl",
|
||||
|
||||
PWD = "pwd",
|
||||
MKDIR = "mkdir",
|
||||
RMDIR = "rmdir",
|
||||
CP = "cp",
|
||||
LS = "ls",
|
||||
RM = "rm",
|
||||
FIND = "find",
|
||||
TEST = "test",
|
||||
CHMOD = "chmod",
|
||||
MKTEMP = "mktemp",
|
||||
|
||||
ZIP = "zip",
|
||||
UNZIP = "unzip -n",
|
||||
GUNZIP = "gunzip",
|
||||
BUNZIP2 = "bunzip2",
|
||||
TAR = "tar",
|
||||
|
||||
MD5SUM = "md5sum",
|
||||
OPENSSL = "openssl",
|
||||
MD5 = "md5",
|
||||
STAT = "stat",
|
||||
TOUCH = "touch",
|
||||
|
||||
CMAKE = "cmake",
|
||||
SEVENZ = "7z",
|
||||
|
||||
RSYNCFLAGS = "--exclude=.git -Oavz",
|
||||
STATFLAG = "-c '%a'",
|
||||
CURLNOCERTFLAG = "",
|
||||
WGETNOCERTFLAG = "",
|
||||
},
|
||||
|
||||
external_deps_subdirs = site_config.LUAROCKS_EXTERNAL_DEPS_SUBDIRS or {
|
||||
bin = "bin",
|
||||
lib = "lib",
|
||||
include = "include"
|
||||
},
|
||||
runtime_external_deps_subdirs = site_config.LUAROCKS_RUNTIME_EXTERNAL_DEPS_SUBDIRS or {
|
||||
bin = "bin",
|
||||
lib = "lib",
|
||||
include = "include"
|
||||
},
|
||||
|
||||
rocks_provided = {}
|
||||
}
|
||||
|
||||
if cfg.platforms.windows then
|
||||
local full_prefix = (site_config.LUAROCKS_PREFIX or (os.getenv("PROGRAMFILES")..[[\LuaRocks]]))
|
||||
extra_luarocks_module_dir = full_prefix.."/lua/?.lua"
|
||||
|
||||
home_config_file = home_config_file and home_config_file:gsub("\\","/")
|
||||
defaults.fs_use_modules = false
|
||||
defaults.arch = "win32-"..cfg.target_cpu
|
||||
defaults.lib_extension = "dll"
|
||||
defaults.external_lib_extension = "dll"
|
||||
defaults.obj_extension = "obj"
|
||||
defaults.external_deps_dirs = { "c:/external/" }
|
||||
defaults.variables.LUA_BINDIR = site_config.LUA_BINDIR and site_config.LUA_BINDIR:gsub("\\", "/") or "c:/lua"..cfg.lua_version.."/bin"
|
||||
defaults.variables.LUA_INCDIR = site_config.LUA_INCDIR and site_config.LUA_INCDIR:gsub("\\", "/") or "c:/lua"..cfg.lua_version.."/include"
|
||||
defaults.variables.LUA_LIBDIR = site_config.LUA_LIBDIR and site_config.LUA_LIBDIR:gsub("\\", "/") or "c:/lua"..cfg.lua_version.."/lib"
|
||||
|
||||
defaults.makefile = "Makefile.win"
|
||||
defaults.variables.MAKE = "nmake"
|
||||
defaults.variables.CC = "cl"
|
||||
defaults.variables.RC = "rc"
|
||||
defaults.variables.WRAPPER = full_prefix.."\\rclauncher.c"
|
||||
defaults.variables.LD = "link"
|
||||
defaults.variables.MT = "mt"
|
||||
defaults.variables.LUALIB = "lua"..cfg.lua_version..".lib"
|
||||
defaults.variables.CFLAGS = "/nologo /MD /O2"
|
||||
defaults.variables.LIBFLAG = "/nologo /dll"
|
||||
|
||||
local bins = { "SEVENZ", "CP", "FIND", "LS", "MD5SUM",
|
||||
"MKDIR", "MV", "PWD", "RMDIR", "TEST", "UNAME", "WGET" }
|
||||
for _, var in ipairs(bins) do
|
||||
if defaults.variables[var] then
|
||||
defaults.variables[var] = full_prefix.."\\tools\\"..defaults.variables[var]
|
||||
end
|
||||
end
|
||||
|
||||
defaults.external_deps_patterns = {
|
||||
bin = { "?.exe", "?.bat" },
|
||||
lib = { "?.lib", "?.dll", "lib?.dll" },
|
||||
include = { "?.h" }
|
||||
}
|
||||
defaults.runtime_external_deps_patterns = {
|
||||
bin = { "?.exe", "?.bat" },
|
||||
lib = { "?.dll", "lib?.dll" },
|
||||
include = { "?.h" }
|
||||
}
|
||||
defaults.export_path = "SET PATH=%s"
|
||||
defaults.export_path_separator = ";"
|
||||
defaults.export_lua_path = "SET LUA_PATH=%s"
|
||||
defaults.export_lua_cpath = "SET LUA_CPATH=%s"
|
||||
defaults.wrapper_suffix = ".bat"
|
||||
|
||||
local localappdata = os.getenv("LOCALAPPDATA")
|
||||
if not localappdata then
|
||||
-- for Windows versions below Vista
|
||||
localappdata = os.getenv("USERPROFILE").."/Local Settings/Application Data"
|
||||
end
|
||||
defaults.local_cache = localappdata.."/LuaRocks/Cache"
|
||||
defaults.web_browser = "start"
|
||||
end
|
||||
|
||||
if cfg.platforms.mingw32 then
|
||||
defaults.obj_extension = "o"
|
||||
defaults.cmake_generator = "MinGW Makefiles"
|
||||
defaults.variables.MAKE = "mingw32-make"
|
||||
defaults.variables.CC = "mingw32-gcc"
|
||||
defaults.variables.RC = "windres"
|
||||
defaults.variables.LD = "mingw32-gcc"
|
||||
defaults.variables.CFLAGS = "-O2"
|
||||
defaults.variables.LIBFLAG = "-shared"
|
||||
defaults.makefile = "Makefile"
|
||||
defaults.external_deps_patterns = {
|
||||
bin = { "?.exe", "?.bat" },
|
||||
-- mingw lookup list from http://stackoverflow.com/a/15853231/1793220
|
||||
-- ...should we keep ?.lib at the end? It's not in the above list.
|
||||
lib = { "lib?.dll.a", "?.dll.a", "lib?.a", "cyg?.dll", "lib?.dll", "?.dll", "?.lib" },
|
||||
include = { "?.h" }
|
||||
}
|
||||
defaults.runtime_external_deps_patterns = {
|
||||
bin = { "?.exe", "?.bat" },
|
||||
lib = { "cyg?.dll", "?.dll", "lib?.dll" },
|
||||
include = { "?.h" }
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
if cfg.platforms.unix then
|
||||
defaults.lib_extension = "so"
|
||||
defaults.external_lib_extension = "so"
|
||||
defaults.obj_extension = "o"
|
||||
defaults.external_deps_dirs = { "/usr/local", "/usr" }
|
||||
defaults.variables.LUA_BINDIR = site_config.LUA_BINDIR or "/usr/local/bin"
|
||||
defaults.variables.LUA_INCDIR = site_config.LUA_INCDIR or "/usr/local/include"
|
||||
defaults.variables.LUA_LIBDIR = site_config.LUA_LIBDIR or "/usr/local/lib"
|
||||
defaults.variables.CFLAGS = "-O2"
|
||||
defaults.cmake_generator = "Unix Makefiles"
|
||||
defaults.variables.CC = "gcc"
|
||||
defaults.variables.LD = "gcc"
|
||||
defaults.gcc_rpath = true
|
||||
defaults.variables.LIBFLAG = "-shared"
|
||||
defaults.external_deps_patterns = {
|
||||
bin = { "?" },
|
||||
lib = { "lib?.a", "lib?.so", "lib?.so.*" },
|
||||
include = { "?.h" }
|
||||
}
|
||||
defaults.runtime_external_deps_patterns = {
|
||||
bin = { "?" },
|
||||
lib = { "lib?.so", "lib?.so.*" },
|
||||
include = { "?.h" }
|
||||
}
|
||||
defaults.export_path = "export PATH='%s'"
|
||||
defaults.export_path_separator = ":"
|
||||
defaults.export_lua_path = "export LUA_PATH='%s'"
|
||||
defaults.export_lua_cpath = "export LUA_CPATH='%s'"
|
||||
defaults.wrapper_suffix = ""
|
||||
defaults.local_cache = cfg.home.."/.cache/luarocks"
|
||||
if not defaults.variables.CFLAGS:match("-fPIC") then
|
||||
defaults.variables.CFLAGS = defaults.variables.CFLAGS.." -fPIC"
|
||||
end
|
||||
defaults.web_browser = "xdg-open"
|
||||
end
|
||||
|
||||
if cfg.platforms.cygwin then
|
||||
defaults.lib_extension = "so" -- can be overridden in the config file for mingw builds
|
||||
defaults.arch = "cygwin-"..cfg.target_cpu
|
||||
defaults.cmake_generator = "Unix Makefiles"
|
||||
defaults.variables.CC = "echo -llua | xargs gcc"
|
||||
defaults.variables.LD = "echo -llua | xargs gcc"
|
||||
defaults.variables.LIBFLAG = "-shared"
|
||||
defaults.link_lua_explicitly = true
|
||||
end
|
||||
|
||||
if cfg.platforms.msys then
|
||||
-- msys is basically cygwin made out of mingw, meaning the subsytem is unixish
|
||||
-- enough, yet we can freely mix with native win32
|
||||
defaults.external_deps_patterns = {
|
||||
bin = { "?.exe", "?.bat", "?" },
|
||||
lib = { "lib?.so", "lib?.so.*", "lib?.dll.a", "?.dll.a",
|
||||
"lib?.a", "lib?.dll", "?.dll", "?.lib" },
|
||||
include = { "?.h" }
|
||||
}
|
||||
defaults.runtime_external_deps_patterns = {
|
||||
bin = { "?.exe", "?.bat" },
|
||||
lib = { "lib?.so", "?.dll", "lib?.dll" },
|
||||
include = { "?.h" }
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
if cfg.platforms.bsd then
|
||||
defaults.variables.MAKE = "gmake"
|
||||
defaults.variables.STATFLAG = "-f '%OLp'"
|
||||
end
|
||||
|
||||
if cfg.platforms.macosx then
|
||||
defaults.variables.MAKE = "make"
|
||||
defaults.external_lib_extension = "dylib"
|
||||
defaults.arch = "macosx-"..cfg.target_cpu
|
||||
defaults.variables.LIBFLAG = "-bundle -undefined dynamic_lookup -all_load"
|
||||
defaults.variables.STAT = "/usr/bin/stat"
|
||||
defaults.variables.STATFLAG = "-f '%A'"
|
||||
local version = io.popen("sw_vers -productVersion"):read("*l")
|
||||
version = tonumber(version and version:match("^[^.]+%.([^.]+)")) or 3
|
||||
if version >= 10 then
|
||||
version = 8
|
||||
elseif version >= 5 then
|
||||
version = 5
|
||||
else
|
||||
defaults.gcc_rpath = false
|
||||
end
|
||||
defaults.variables.CC = "env MACOSX_DEPLOYMENT_TARGET=10."..version.." gcc"
|
||||
defaults.variables.LD = "env MACOSX_DEPLOYMENT_TARGET=10."..version.." gcc"
|
||||
defaults.web_browser = "open"
|
||||
end
|
||||
|
||||
if cfg.platforms.linux then
|
||||
defaults.arch = "linux-"..cfg.target_cpu
|
||||
end
|
||||
|
||||
if cfg.platforms.freebsd then
|
||||
defaults.arch = "freebsd-"..cfg.target_cpu
|
||||
defaults.gcc_rpath = false
|
||||
defaults.variables.CC = "cc"
|
||||
defaults.variables.LD = "cc"
|
||||
end
|
||||
|
||||
if cfg.platforms.openbsd then
|
||||
defaults.arch = "openbsd-"..cfg.target_cpu
|
||||
end
|
||||
|
||||
if cfg.platforms.netbsd then
|
||||
defaults.arch = "netbsd-"..cfg.target_cpu
|
||||
end
|
||||
|
||||
if cfg.platforms.solaris then
|
||||
defaults.arch = "solaris-"..cfg.target_cpu
|
||||
--defaults.platforms = {"unix", "solaris"}
|
||||
defaults.variables.MAKE = "gmake"
|
||||
end
|
||||
|
||||
-- Expose some more values detected by LuaRocks for use by rockspec authors.
|
||||
defaults.variables.LIB_EXTENSION = defaults.lib_extension
|
||||
defaults.variables.OBJ_EXTENSION = defaults.obj_extension
|
||||
defaults.variables.LUAROCKS_PREFIX = site_config.LUAROCKS_PREFIX
|
||||
defaults.variables.LUA = site_config.LUA_DIR_SET and (defaults.variables.LUA_BINDIR.."/"..defaults.lua_interpreter) or defaults.lua_interpreter
|
||||
|
||||
-- Add built-in modules to rocks_provided
|
||||
defaults.rocks_provided["lua"] = cfg.lua_version.."-1"
|
||||
|
||||
if bit32 then -- Lua 5.2+
|
||||
defaults.rocks_provided["bit32"] = cfg.lua_version.."-1"
|
||||
end
|
||||
|
||||
if utf8 then -- Lua 5.3+
|
||||
defaults.rocks_provided["utf8"] = cfg.lua_version.."-1"
|
||||
end
|
||||
|
||||
if package.loaded.jit then
|
||||
-- LuaJIT
|
||||
local lj_version = package.loaded.jit.version:match("LuaJIT (.*)"):gsub("%-","")
|
||||
--defaults.rocks_provided["luajit"] = lj_version.."-1"
|
||||
defaults.rocks_provided["luabitop"] = lj_version.."-1"
|
||||
end
|
||||
|
||||
-- Use defaults:
|
||||
|
||||
-- Populate some arrays with values from their 'defaults' counterparts
|
||||
-- if they were not already set by user.
|
||||
for _, entry in ipairs({"variables", "rocks_provided"}) do
|
||||
if not cfg[entry] then
|
||||
cfg[entry] = {}
|
||||
end
|
||||
for k,v in pairs(defaults[entry]) do
|
||||
if not cfg[entry][k] then
|
||||
cfg[entry][k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- For values not set in the config file, use values from the 'defaults' table.
|
||||
local cfg_mt = {
|
||||
__index = function(t, k)
|
||||
local default = defaults[k]
|
||||
if default then
|
||||
rawset(t, k, default)
|
||||
end
|
||||
return default
|
||||
end
|
||||
}
|
||||
setmetatable(cfg, cfg_mt)
|
||||
|
||||
if not cfg.check_certificates then
|
||||
cfg.variables.CURLNOCERTFLAG = "-k"
|
||||
cfg.variables.WGETNOCERTFLAG = "--no-check-certificate"
|
||||
end
|
||||
|
||||
function cfg.make_paths_from_tree(tree)
|
||||
local lua_path, lib_path, bin_path
|
||||
if type(tree) == "string" then
|
||||
lua_path = tree..cfg.lua_modules_path
|
||||
lib_path = tree..cfg.lib_modules_path
|
||||
bin_path = tree.."/bin"
|
||||
else
|
||||
lua_path = tree.lua_dir or tree.root..cfg.lua_modules_path
|
||||
lib_path = tree.lib_dir or tree.root..cfg.lib_modules_path
|
||||
bin_path = tree.bin_dir or tree.root.."/bin"
|
||||
end
|
||||
return lua_path, lib_path, bin_path
|
||||
end
|
||||
|
||||
function cfg.package_paths(current)
|
||||
local new_path, new_cpath, new_bin = {}, {}, {}
|
||||
local function add_tree_to_paths(tree)
|
||||
local lua_path, lib_path, bin_path = cfg.make_paths_from_tree(tree)
|
||||
table.insert(new_path, lua_path.."/?.lua")
|
||||
table.insert(new_path, lua_path.."/?/init.lua")
|
||||
table.insert(new_cpath, lib_path.."/?."..cfg.lib_extension)
|
||||
table.insert(new_bin, bin_path)
|
||||
end
|
||||
if current then
|
||||
add_tree_to_paths(current)
|
||||
end
|
||||
for _,tree in ipairs(cfg.rocks_trees) do
|
||||
add_tree_to_paths(tree)
|
||||
end
|
||||
if extra_luarocks_module_dir then
|
||||
table.insert(new_path, extra_luarocks_module_dir)
|
||||
end
|
||||
return table.concat(new_path, ";"), table.concat(new_cpath, ";"), table.concat(new_bin, cfg.export_path_separator)
|
||||
end
|
||||
|
||||
function cfg.init_package_paths()
|
||||
local lr_path, lr_cpath, lr_bin = cfg.package_paths()
|
||||
package.path = util.remove_path_dupes(package.path .. ";" .. lr_path, ";")
|
||||
package.cpath = util.remove_path_dupes(package.cpath .. ";" .. lr_cpath, ";")
|
||||
end
|
||||
|
||||
function cfg.which_config()
|
||||
local ret = {
|
||||
system = {
|
||||
file = sys_config_file or sys_config_file_default,
|
||||
ok = sys_config_ok,
|
||||
},
|
||||
user = {
|
||||
file = home_config_file or home_config_file_default,
|
||||
ok = home_config_ok,
|
||||
}
|
||||
}
|
||||
ret.nearest = (ret.user.ok and ret.user.file) or ret.system.file
|
||||
return ret
|
||||
end
|
||||
|
||||
cfg.user_agent = "LuaRocks/"..cfg.program_version.." "..cfg.arch
|
||||
|
||||
cfg.http_proxy = os.getenv("http_proxy")
|
||||
cfg.https_proxy = os.getenv("https_proxy")
|
||||
cfg.no_proxy = os.getenv("no_proxy")
|
||||
|
||||
--- Check if platform was detected
|
||||
-- @param query string: The platform name to check.
|
||||
-- @return boolean: true if LuaRocks is currently running on queried platform.
|
||||
function cfg.is_platform(query)
|
||||
assert(type(query) == "string")
|
||||
|
||||
for _, platform in ipairs(cfg.platforms) do
|
||||
if platform == query then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return cfg
|
||||
199
Utils/luarocks/lua/luarocks/command_line.lua
Normal file
199
Utils/luarocks/lua/luarocks/command_line.lua
Normal file
@ -0,0 +1,199 @@
|
||||
|
||||
--- Functions for command-line scripts.
|
||||
local command_line = {}
|
||||
|
||||
local unpack = unpack or table.unpack
|
||||
|
||||
local util = require("luarocks.util")
|
||||
local cfg = require("luarocks.cfg")
|
||||
local path = require("luarocks.path")
|
||||
local dir = require("luarocks.dir")
|
||||
local deps = require("luarocks.deps")
|
||||
local fs = require("luarocks.fs")
|
||||
|
||||
local program = util.this_program("luarocks")
|
||||
|
||||
local function error_handler(err)
|
||||
return debug.traceback("LuaRocks "..cfg.program_version..
|
||||
" bug (please report at https://github.com/keplerproject/luarocks/issues).\n"..err, 2)
|
||||
end
|
||||
|
||||
--- Display an error message and exit.
|
||||
-- @param message string: The error message.
|
||||
-- @param exitcode number: the exitcode to use
|
||||
local function die(message, exitcode)
|
||||
assert(type(message) == "string")
|
||||
util.printerr("\nError: "..message)
|
||||
|
||||
local ok, err = xpcall(util.run_scheduled_functions, error_handler)
|
||||
if not ok then
|
||||
util.printerr("\nError: "..err)
|
||||
exitcode = cfg.errorcodes.CRASH
|
||||
end
|
||||
|
||||
os.exit(exitcode or cfg.errorcodes.UNSPECIFIED)
|
||||
end
|
||||
|
||||
local function replace_tree(flags, tree)
|
||||
tree = dir.normalize(tree)
|
||||
flags["tree"] = tree
|
||||
path.use_tree(tree)
|
||||
end
|
||||
|
||||
--- Main command-line processor.
|
||||
-- Parses input arguments and calls the appropriate driver function
|
||||
-- to execute the action requested on the command-line, forwarding
|
||||
-- to it any additional arguments passed by the user.
|
||||
-- Uses the global table "commands", which contains
|
||||
-- the loaded modules representing commands.
|
||||
-- @param ... string: Arguments given on the command-line.
|
||||
function command_line.run_command(...)
|
||||
local args = {...}
|
||||
local cmdline_vars = {}
|
||||
for i = #args, 1, -1 do
|
||||
local arg = args[i]
|
||||
if arg:match("^[^-][^=]*=") then
|
||||
local var, val = arg:match("^([A-Z_][A-Z0-9_]*)=(.*)")
|
||||
if val then
|
||||
cmdline_vars[var] = val
|
||||
table.remove(args, i)
|
||||
else
|
||||
die("Invalid assignment: "..arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
local nonflags = { util.parse_flags(unpack(args)) }
|
||||
local flags = table.remove(nonflags, 1)
|
||||
if flags.ERROR then
|
||||
die(flags.ERROR.." See --help.")
|
||||
end
|
||||
|
||||
if flags["from"] then flags["server"] = flags["from"] end
|
||||
if flags["only-from"] then flags["only-server"] = flags["only-from"] end
|
||||
if flags["only-sources-from"] then flags["only-sources"] = flags["only-sources-from"] end
|
||||
if flags["to"] then flags["tree"] = flags["to"] end
|
||||
if flags["nodeps"] then
|
||||
flags["deps-mode"] = "none"
|
||||
end
|
||||
|
||||
cfg.flags = flags
|
||||
|
||||
local command
|
||||
|
||||
if flags["verbose"] then -- setting it in the config file will kick-in earlier in the process
|
||||
cfg.verbose = true
|
||||
fs.verbose()
|
||||
end
|
||||
|
||||
if flags["timeout"] then -- setting it in the config file will kick-in earlier in the process
|
||||
local timeout = tonumber(flags["timeout"])
|
||||
if timeout then
|
||||
cfg.connection_timeout = timeout
|
||||
else
|
||||
die "Argument error: --timeout expects a numeric argument."
|
||||
end
|
||||
end
|
||||
|
||||
if flags["version"] then
|
||||
util.printout(program.." "..cfg.program_version)
|
||||
util.printout(program_description)
|
||||
util.printout()
|
||||
os.exit(cfg.errorcodes.OK)
|
||||
elseif flags["help"] or #nonflags == 0 then
|
||||
command = "help"
|
||||
else
|
||||
command = table.remove(nonflags, 1)
|
||||
end
|
||||
command = command:gsub("-", "_")
|
||||
|
||||
if cfg.local_by_default then
|
||||
flags["local"] = true
|
||||
end
|
||||
|
||||
if flags["deps-mode"] and not deps.check_deps_mode_flag(flags["deps-mode"]) then
|
||||
die("Invalid entry for --deps-mode.")
|
||||
end
|
||||
|
||||
if flags["branch"] then
|
||||
cfg.branch = flags["branch"]
|
||||
end
|
||||
|
||||
if flags["tree"] then
|
||||
local named = false
|
||||
for _, tree in ipairs(cfg.rocks_trees) do
|
||||
if type(tree) == "table" and flags["tree"] == tree.name then
|
||||
if not tree.root then
|
||||
die("Configuration error: tree '"..tree.name.."' has no 'root' field.")
|
||||
end
|
||||
replace_tree(flags, tree.root)
|
||||
named = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not named then
|
||||
local root_dir = fs.absolute_name(flags["tree"])
|
||||
replace_tree(flags, root_dir)
|
||||
end
|
||||
elseif flags["local"] then
|
||||
if not cfg.home_tree then
|
||||
die("The --local flag is meant for operating in a user's home directory.\n"..
|
||||
"You are running as a superuser, which is intended for system-wide operation.\n"..
|
||||
"To force using the superuser's home, use --tree explicitly.")
|
||||
end
|
||||
replace_tree(flags, cfg.home_tree)
|
||||
else
|
||||
local trees = cfg.rocks_trees
|
||||
path.use_tree(trees[#trees])
|
||||
end
|
||||
|
||||
if type(cfg.root_dir) == "string" then
|
||||
cfg.root_dir = cfg.root_dir:gsub("/+$", "")
|
||||
else
|
||||
cfg.root_dir.root = cfg.root_dir.root:gsub("/+$", "")
|
||||
end
|
||||
cfg.rocks_dir = cfg.rocks_dir:gsub("/+$", "")
|
||||
cfg.deploy_bin_dir = cfg.deploy_bin_dir:gsub("/+$", "")
|
||||
cfg.deploy_lua_dir = cfg.deploy_lua_dir:gsub("/+$", "")
|
||||
cfg.deploy_lib_dir = cfg.deploy_lib_dir:gsub("/+$", "")
|
||||
|
||||
cfg.variables.ROCKS_TREE = cfg.rocks_dir
|
||||
cfg.variables.SCRIPTS_DIR = cfg.deploy_bin_dir
|
||||
|
||||
if flags["server"] then
|
||||
local protocol, path = dir.split_url(flags["server"])
|
||||
table.insert(cfg.rocks_servers, 1, protocol.."://"..path)
|
||||
end
|
||||
|
||||
if flags["only-server"] then
|
||||
cfg.rocks_servers = { flags["only-server"] }
|
||||
end
|
||||
|
||||
if flags["only-sources"] then
|
||||
cfg.only_sources_from = flags["only-sources"]
|
||||
end
|
||||
|
||||
if command ~= "help" then
|
||||
for k, v in pairs(cmdline_vars) do
|
||||
cfg.variables[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
if not fs.current_dir() or fs.current_dir() == "" then
|
||||
die("Current directory does not exist. Please run LuaRocks from an existing directory.")
|
||||
end
|
||||
|
||||
if commands[command] then
|
||||
local cmd = require(commands[command])
|
||||
local call_ok, ok, err, exitcode = xpcall(function() return cmd.command(flags, unpack(nonflags)) end, error_handler)
|
||||
if not call_ok then
|
||||
die(ok, cfg.errorcodes.CRASH)
|
||||
elseif not ok then
|
||||
die(err, exitcode)
|
||||
end
|
||||
else
|
||||
die("Unknown command: "..command)
|
||||
end
|
||||
util.run_scheduled_functions()
|
||||
end
|
||||
|
||||
return command_line
|
||||
72
Utils/luarocks/lua/luarocks/config_cmd.lua
Normal file
72
Utils/luarocks/lua/luarocks/config_cmd.lua
Normal file
@ -0,0 +1,72 @@
|
||||
--- Module implementing the LuaRocks "config" command.
|
||||
-- Queries information about the LuaRocks configuration.
|
||||
local config_cmd = {}
|
||||
|
||||
local cfg = require("luarocks.cfg")
|
||||
local util = require("luarocks.util")
|
||||
local dir = require("luarocks.dir")
|
||||
|
||||
util.add_run_function(config_cmd)
|
||||
config_cmd.help_summary = "Query information about the LuaRocks configuration."
|
||||
config_cmd.help_arguments = "<flag>"
|
||||
config_cmd.help = [[
|
||||
--lua-incdir Path to Lua header files.
|
||||
|
||||
--lua-libdir Path to Lua library files.
|
||||
|
||||
--lua-ver Lua version (in major.minor format). e.g. 5.1
|
||||
|
||||
--system-config Location of the system config file.
|
||||
|
||||
--user-config Location of the user config file.
|
||||
|
||||
--rock-trees Rocks trees in use. First the user tree, then the system tree.
|
||||
]]
|
||||
|
||||
local function config_file(conf)
|
||||
print(dir.normalize(conf.file))
|
||||
if conf.ok then
|
||||
return true
|
||||
else
|
||||
return nil, "file not found"
|
||||
end
|
||||
end
|
||||
|
||||
--- Driver function for "config" command.
|
||||
-- @return boolean: True if succeeded, nil on errors.
|
||||
function config_cmd.command(flags)
|
||||
if flags["lua-incdir"] then
|
||||
print(cfg.variables.LUA_INCDIR)
|
||||
return true
|
||||
end
|
||||
if flags["lua-libdir"] then
|
||||
print(cfg.variables.LUA_LIBDIR)
|
||||
return true
|
||||
end
|
||||
if flags["lua-ver"] then
|
||||
print(cfg.lua_version)
|
||||
return true
|
||||
end
|
||||
local conf = cfg.which_config()
|
||||
if flags["system-config"] then
|
||||
return config_file(conf.system)
|
||||
end
|
||||
if flags["user-config"] then
|
||||
return config_file(conf.user)
|
||||
end
|
||||
if flags["rock-trees"] then
|
||||
for _, tree in ipairs(cfg.rocks_trees) do
|
||||
if type(tree) == "string" then
|
||||
util.printout(dir.normalize(tree))
|
||||
else
|
||||
local name = tree.name and "\t"..tree.name or ""
|
||||
util.printout(dir.normalize(tree.root)..name)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return nil, "Please provide a flag for querying configuration values. "..util.see_help("config")
|
||||
end
|
||||
|
||||
return config_cmd
|
||||
752
Utils/luarocks/lua/luarocks/deps.lua
Normal file
752
Utils/luarocks/lua/luarocks/deps.lua
Normal file
@ -0,0 +1,752 @@
|
||||
|
||||
--- Dependency handling functions.
|
||||
-- Dependencies are represented in LuaRocks through strings with
|
||||
-- a package name followed by a comma-separated list of constraints.
|
||||
-- Each constraint consists of an operator and a version number.
|
||||
-- In this string format, version numbers are represented as
|
||||
-- naturally as possible, like they are used by upstream projects
|
||||
-- (e.g. "2.0beta3"). Internally, LuaRocks converts them to a purely
|
||||
-- numeric representation, allowing comparison following some
|
||||
-- "common sense" heuristics. The precise specification of the
|
||||
-- comparison criteria is the source code of this module, but the
|
||||
-- test/test_deps.lua file included with LuaRocks provides some
|
||||
-- insights on what these criteria are.
|
||||
local deps = {}
|
||||
package.loaded["luarocks.deps"] = deps
|
||||
|
||||
local cfg = require("luarocks.cfg")
|
||||
local manif_core = require("luarocks.manif_core")
|
||||
local path = require("luarocks.path")
|
||||
local dir = require("luarocks.dir")
|
||||
local util = require("luarocks.util")
|
||||
|
||||
local operators = {
|
||||
["=="] = "==",
|
||||
["~="] = "~=",
|
||||
[">"] = ">",
|
||||
["<"] = "<",
|
||||
[">="] = ">=",
|
||||
["<="] = "<=",
|
||||
["~>"] = "~>",
|
||||
-- plus some convenience translations
|
||||
[""] = "==",
|
||||
["="] = "==",
|
||||
["!="] = "~="
|
||||
}
|
||||
|
||||
local deltas = {
|
||||
scm = 1100,
|
||||
cvs = 1000,
|
||||
rc = -1000,
|
||||
pre = -10000,
|
||||
beta = -100000,
|
||||
alpha = -1000000
|
||||
}
|
||||
|
||||
local version_mt = {
|
||||
--- Equality comparison for versions.
|
||||
-- All version numbers must be equal.
|
||||
-- If both versions have revision numbers, they must be equal;
|
||||
-- otherwise the revision number is ignored.
|
||||
-- @param v1 table: version table to compare.
|
||||
-- @param v2 table: version table to compare.
|
||||
-- @return boolean: true if they are considered equivalent.
|
||||
__eq = function(v1, v2)
|
||||
if #v1 ~= #v2 then
|
||||
return false
|
||||
end
|
||||
for i = 1, #v1 do
|
||||
if v1[i] ~= v2[i] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
if v1.revision and v2.revision then
|
||||
return (v1.revision == v2.revision)
|
||||
end
|
||||
return true
|
||||
end,
|
||||
--- Size comparison for versions.
|
||||
-- All version numbers are compared.
|
||||
-- If both versions have revision numbers, they are compared;
|
||||
-- otherwise the revision number is ignored.
|
||||
-- @param v1 table: version table to compare.
|
||||
-- @param v2 table: version table to compare.
|
||||
-- @return boolean: true if v1 is considered lower than v2.
|
||||
__lt = function(v1, v2)
|
||||
for i = 1, math.max(#v1, #v2) do
|
||||
local v1i, v2i = v1[i] or 0, v2[i] or 0
|
||||
if v1i ~= v2i then
|
||||
return (v1i < v2i)
|
||||
end
|
||||
end
|
||||
if v1.revision and v2.revision then
|
||||
return (v1.revision < v2.revision)
|
||||
end
|
||||
return false
|
||||
end
|
||||
}
|
||||
|
||||
local version_cache = {}
|
||||
setmetatable(version_cache, {
|
||||
__mode = "kv"
|
||||
})
|
||||
|
||||
--- Parse a version string, converting to table format.
|
||||
-- A version table contains all components of the version string
|
||||
-- converted to numeric format, stored in the array part of the table.
|
||||
-- If the version contains a revision, it is stored numerically
|
||||
-- in the 'revision' field. The original string representation of
|
||||
-- the string is preserved in the 'string' field.
|
||||
-- Returned version tables use a metatable
|
||||
-- allowing later comparison through relational operators.
|
||||
-- @param vstring string: A version number in string format.
|
||||
-- @return table or nil: A version table or nil
|
||||
-- if the input string contains invalid characters.
|
||||
function deps.parse_version(vstring)
|
||||
if not vstring then return nil end
|
||||
assert(type(vstring) == "string")
|
||||
|
||||
local cached = version_cache[vstring]
|
||||
if cached then
|
||||
return cached
|
||||
end
|
||||
|
||||
local version = {}
|
||||
local i = 1
|
||||
|
||||
local function add_token(number)
|
||||
version[i] = version[i] and version[i] + number/100000 or number
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
-- trim leading and trailing spaces
|
||||
vstring = vstring:match("^%s*(.*)%s*$")
|
||||
version.string = vstring
|
||||
-- store revision separately if any
|
||||
local main, revision = vstring:match("(.*)%-(%d+)$")
|
||||
if revision then
|
||||
vstring = main
|
||||
version.revision = tonumber(revision)
|
||||
end
|
||||
while #vstring > 0 do
|
||||
-- extract a number
|
||||
local token, rest = vstring:match("^(%d+)[%.%-%_]*(.*)")
|
||||
if token then
|
||||
add_token(tonumber(token))
|
||||
else
|
||||
-- extract a word
|
||||
token, rest = vstring:match("^(%a+)[%.%-%_]*(.*)")
|
||||
if not token then
|
||||
util.printerr("Warning: version number '"..vstring.."' could not be parsed.")
|
||||
version[i] = 0
|
||||
break
|
||||
end
|
||||
version[i] = deltas[token] or (token:byte() / 1000)
|
||||
end
|
||||
vstring = rest
|
||||
end
|
||||
setmetatable(version, version_mt)
|
||||
version_cache[vstring] = version
|
||||
return version
|
||||
end
|
||||
|
||||
--- Utility function to compare version numbers given as strings.
|
||||
-- @param a string: one version.
|
||||
-- @param b string: another version.
|
||||
-- @return boolean: True if a > b.
|
||||
function deps.compare_versions(a, b)
|
||||
return deps.parse_version(a) > deps.parse_version(b)
|
||||
end
|
||||
|
||||
--- Consumes a constraint from a string, converting it to table format.
|
||||
-- For example, a string ">= 1.0, > 2.0" is converted to a table in the
|
||||
-- format {op = ">=", version={1,0}} and the rest, "> 2.0", is returned
|
||||
-- back to the caller.
|
||||
-- @param input string: A list of constraints in string format.
|
||||
-- @return (table, string) or nil: A table representing the same
|
||||
-- constraints and the string with the unused input, or nil if the
|
||||
-- input string is invalid.
|
||||
local function parse_constraint(input)
|
||||
assert(type(input) == "string")
|
||||
|
||||
local no_upgrade, op, version, rest = input:match("^(@?)([<>=~!]*)%s*([%w%.%_%-]+)[%s,]*(.*)")
|
||||
local _op = operators[op]
|
||||
version = deps.parse_version(version)
|
||||
if not _op then
|
||||
return nil, "Encountered bad constraint operator: '"..tostring(op).."' in '"..input.."'"
|
||||
end
|
||||
if not version then
|
||||
return nil, "Could not parse version from constraint: '"..input.."'"
|
||||
end
|
||||
return { op = _op, version = version, no_upgrade = no_upgrade=="@" and true or nil }, rest
|
||||
end
|
||||
|
||||
--- Convert a list of constraints from string to table format.
|
||||
-- For example, a string ">= 1.0, < 2.0" is converted to a table in the format
|
||||
-- {{op = ">=", version={1,0}}, {op = "<", version={2,0}}}.
|
||||
-- Version tables use a metatable allowing later comparison through
|
||||
-- relational operators.
|
||||
-- @param input string: A list of constraints in string format.
|
||||
-- @return table or nil: A table representing the same constraints,
|
||||
-- or nil if the input string is invalid.
|
||||
function deps.parse_constraints(input)
|
||||
assert(type(input) == "string")
|
||||
|
||||
local constraints, constraint, oinput = {}, nil, input
|
||||
while #input > 0 do
|
||||
constraint, input = parse_constraint(input)
|
||||
if constraint then
|
||||
table.insert(constraints, constraint)
|
||||
else
|
||||
return nil, "Failed to parse constraint '"..tostring(oinput).."' with error: ".. input
|
||||
end
|
||||
end
|
||||
return constraints
|
||||
end
|
||||
|
||||
--- Convert a dependency from string to table format.
|
||||
-- For example, a string "foo >= 1.0, < 2.0"
|
||||
-- is converted to a table in the format
|
||||
-- {name = "foo", constraints = {{op = ">=", version={1,0}},
|
||||
-- {op = "<", version={2,0}}}}. Version tables use a metatable
|
||||
-- allowing later comparison through relational operators.
|
||||
-- @param dep string: A dependency in string format
|
||||
-- as entered in rockspec files.
|
||||
-- @return table or nil: A table representing the same dependency relation,
|
||||
-- or nil if the input string is invalid.
|
||||
function deps.parse_dep(dep)
|
||||
assert(type(dep) == "string")
|
||||
|
||||
local name, rest = dep:match("^%s*([a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*(.*)")
|
||||
if not name then return nil, "failed to extract dependency name from '"..tostring(dep).."'" end
|
||||
local constraints, err = deps.parse_constraints(rest)
|
||||
if not constraints then return nil, err end
|
||||
return { name = name, constraints = constraints }
|
||||
end
|
||||
|
||||
--- Convert a version table to a string.
|
||||
-- @param v table: The version table
|
||||
-- @param internal boolean or nil: Whether to display versions in their
|
||||
-- internal representation format or how they were specified.
|
||||
-- @return string: The dependency information pretty-printed as a string.
|
||||
function deps.show_version(v, internal)
|
||||
assert(type(v) == "table")
|
||||
assert(type(internal) == "boolean" or not internal)
|
||||
|
||||
return (internal
|
||||
and table.concat(v, ":")..(v.revision and tostring(v.revision) or "")
|
||||
or v.string)
|
||||
end
|
||||
|
||||
--- Convert a dependency in table format to a string.
|
||||
-- @param dep table: The dependency in table format
|
||||
-- @param internal boolean or nil: Whether to display versions in their
|
||||
-- internal representation format or how they were specified.
|
||||
-- @return string: The dependency information pretty-printed as a string.
|
||||
function deps.show_dep(dep, internal)
|
||||
assert(type(dep) == "table")
|
||||
assert(type(internal) == "boolean" or not internal)
|
||||
|
||||
if #dep.constraints > 0 then
|
||||
local pretty = {}
|
||||
for _, c in ipairs(dep.constraints) do
|
||||
table.insert(pretty, c.op .. " " .. deps.show_version(c.version, internal))
|
||||
end
|
||||
return dep.name.." "..table.concat(pretty, ", ")
|
||||
else
|
||||
return dep.name
|
||||
end
|
||||
end
|
||||
|
||||
--- A more lenient check for equivalence between versions.
|
||||
-- This returns true if the requested components of a version
|
||||
-- match and ignore the ones that were not given. For example,
|
||||
-- when requesting "2", then "2", "2.1", "2.3.5-9"... all match.
|
||||
-- When requesting "2.1", then "2.1", "2.1.3" match, but "2.2"
|
||||
-- doesn't.
|
||||
-- @param version string or table: Version to be tested; may be
|
||||
-- in string format or already parsed into a table.
|
||||
-- @param requested string or table: Version requested; may be
|
||||
-- in string format or already parsed into a table.
|
||||
-- @return boolean: True if the tested version matches the requested
|
||||
-- version, false otherwise.
|
||||
local function partial_match(version, requested)
|
||||
assert(type(version) == "string" or type(version) == "table")
|
||||
assert(type(requested) == "string" or type(version) == "table")
|
||||
|
||||
if type(version) ~= "table" then version = deps.parse_version(version) end
|
||||
if type(requested) ~= "table" then requested = deps.parse_version(requested) end
|
||||
if not version or not requested then return false end
|
||||
|
||||
for i, ri in ipairs(requested) do
|
||||
local vi = version[i] or 0
|
||||
if ri ~= vi then return false end
|
||||
end
|
||||
if requested.revision then
|
||||
return requested.revision == version.revision
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Check if a version satisfies a set of constraints.
|
||||
-- @param version table: A version in table format
|
||||
-- @param constraints table: An array of constraints in table format.
|
||||
-- @return boolean: True if version satisfies all constraints,
|
||||
-- false otherwise.
|
||||
function deps.match_constraints(version, constraints)
|
||||
assert(type(version) == "table")
|
||||
assert(type(constraints) == "table")
|
||||
local ok = true
|
||||
setmetatable(version, version_mt)
|
||||
for _, constr in pairs(constraints) do
|
||||
if type(constr.version) == "string" then
|
||||
constr.version = deps.parse_version(constr.version)
|
||||
end
|
||||
local constr_version, constr_op = constr.version, constr.op
|
||||
setmetatable(constr_version, version_mt)
|
||||
if constr_op == "==" then ok = version == constr_version
|
||||
elseif constr_op == "~=" then ok = version ~= constr_version
|
||||
elseif constr_op == ">" then ok = version > constr_version
|
||||
elseif constr_op == "<" then ok = version < constr_version
|
||||
elseif constr_op == ">=" then ok = version >= constr_version
|
||||
elseif constr_op == "<=" then ok = version <= constr_version
|
||||
elseif constr_op == "~>" then ok = partial_match(version, constr_version)
|
||||
end
|
||||
if not ok then break end
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
--- Attempt to match a dependency to an installed rock.
|
||||
-- @param dep table: A dependency parsed in table format.
|
||||
-- @param blacklist table: Versions that can't be accepted. Table where keys
|
||||
-- are program versions and values are 'true'.
|
||||
-- @return string or nil: latest installed version of the rock matching the dependency
|
||||
-- or nil if it could not be matched.
|
||||
local function match_dep(dep, blacklist, deps_mode)
|
||||
assert(type(dep) == "table")
|
||||
|
||||
local versions
|
||||
if cfg.rocks_provided[dep.name] then
|
||||
-- provided rocks have higher priority than manifest's rocks
|
||||
versions = { cfg.rocks_provided[dep.name] }
|
||||
else
|
||||
versions = manif_core.get_versions(dep.name, deps_mode)
|
||||
end
|
||||
|
||||
local latest_version
|
||||
for _, vstring in ipairs(versions) do
|
||||
if not blacklist or not blacklist[vstring] then
|
||||
local version = deps.parse_version(vstring)
|
||||
if deps.match_constraints(version, dep.constraints) then
|
||||
if not latest_version or version > latest_version then
|
||||
latest_version = version
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return latest_version and latest_version.string
|
||||
end
|
||||
|
||||
--- Attempt to match dependencies of a rockspec to installed rocks.
|
||||
-- @param rockspec table: The rockspec loaded as a table.
|
||||
-- @param blacklist table or nil: Program versions to not use as valid matches.
|
||||
-- Table where keys are program names and values are tables where keys
|
||||
-- are program versions and values are 'true'.
|
||||
-- @return table, table, table: A table where keys are dependencies parsed
|
||||
-- in table format and values are tables containing fields 'name' and
|
||||
-- version' representing matches; a table of missing dependencies
|
||||
-- parsed as tables; and a table of "no-upgrade" missing dependencies
|
||||
-- (to be used in plugin modules so that a plugin does not force upgrade of
|
||||
-- its parent application).
|
||||
function deps.match_deps(rockspec, blacklist, deps_mode)
|
||||
assert(type(rockspec) == "table")
|
||||
assert(type(blacklist) == "table" or not blacklist)
|
||||
local matched, missing, no_upgrade = {}, {}, {}
|
||||
|
||||
for _, dep in ipairs(rockspec.dependencies) do
|
||||
local found = match_dep(dep, blacklist and blacklist[dep.name] or nil, deps_mode)
|
||||
if found then
|
||||
if not cfg.rocks_provided[dep.name] then
|
||||
matched[dep] = {name = dep.name, version = found}
|
||||
end
|
||||
else
|
||||
if dep.constraints[1] and dep.constraints[1].no_upgrade then
|
||||
no_upgrade[dep.name] = dep
|
||||
else
|
||||
missing[dep.name] = dep
|
||||
end
|
||||
end
|
||||
end
|
||||
return matched, missing, no_upgrade
|
||||
end
|
||||
|
||||
--- Return a set of values of a table.
|
||||
-- @param tbl table: The input table.
|
||||
-- @return table: The array of keys.
|
||||
local function values_set(tbl)
|
||||
local set = {}
|
||||
for _, v in pairs(tbl) do
|
||||
set[v] = true
|
||||
end
|
||||
return set
|
||||
end
|
||||
|
||||
local function rock_status(name, deps_mode)
|
||||
local search = require("luarocks.search")
|
||||
local installed = match_dep(search.make_query(name), nil, deps_mode)
|
||||
local installation_type = cfg.rocks_provided[name] and "provided by VM" or "installed"
|
||||
return installed and installed.." "..installation_type or "not installed"
|
||||
end
|
||||
|
||||
--- Check depenendencies of a package and report any missing ones.
|
||||
-- @param name string: package name.
|
||||
-- @param version string: package version.
|
||||
-- @param dependencies table: array of dependencies.
|
||||
-- @param deps_mode string: Which trees to check dependencies for:
|
||||
-- "one" for the current default tree, "all" for all trees,
|
||||
-- "order" for all trees with priority >= the current default, "none" for no trees.
|
||||
function deps.report_missing_dependencies(name, version, dependencies, deps_mode)
|
||||
local first_missing_dep = true
|
||||
|
||||
for _, dep in ipairs(dependencies) do
|
||||
if not match_dep(dep, nil, deps_mode) then
|
||||
if first_missing_dep then
|
||||
util.printout(("Missing dependencies for %s %s:"):format(name, version))
|
||||
first_missing_dep = false
|
||||
end
|
||||
|
||||
util.printout((" %s (%s)"):format(deps.show_dep(dep), rock_status(dep.name, deps_mode)))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Check dependencies of a rock and attempt to install any missing ones.
|
||||
-- Packages are installed using the LuaRocks "install" command.
|
||||
-- Aborts the program if a dependency could not be fulfilled.
|
||||
-- @param rockspec table: A rockspec in table format.
|
||||
-- @return boolean or (nil, string, [string]): True if no errors occurred, or
|
||||
-- nil and an error message if any test failed, followed by an optional
|
||||
-- error code.
|
||||
function deps.fulfill_dependencies(rockspec, deps_mode)
|
||||
|
||||
local search = require("luarocks.search")
|
||||
local install = require("luarocks.install")
|
||||
|
||||
if rockspec.supported_platforms then
|
||||
if not deps.platforms_set then
|
||||
deps.platforms_set = values_set(cfg.platforms)
|
||||
end
|
||||
local supported = nil
|
||||
for _, plat in pairs(rockspec.supported_platforms) do
|
||||
local neg, plat = plat:match("^(!?)(.*)")
|
||||
if neg == "!" then
|
||||
if deps.platforms_set[plat] then
|
||||
return nil, "This rockspec for "..rockspec.package.." does not support "..plat.." platforms."
|
||||
end
|
||||
else
|
||||
if deps.platforms_set[plat] then
|
||||
supported = true
|
||||
else
|
||||
if supported == nil then
|
||||
supported = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if supported == false then
|
||||
local plats = table.concat(cfg.platforms, ", ")
|
||||
return nil, "This rockspec for "..rockspec.package.." does not support "..plats.." platforms."
|
||||
end
|
||||
end
|
||||
|
||||
deps.report_missing_dependencies(rockspec.name, rockspec.version, rockspec.dependencies, deps_mode)
|
||||
|
||||
local first_missing_dep = true
|
||||
|
||||
for _, dep in ipairs(rockspec.dependencies) do
|
||||
if not match_dep(dep, nil, deps_mode) then
|
||||
if first_missing_dep then
|
||||
util.printout()
|
||||
first_missing_dep = false
|
||||
end
|
||||
|
||||
util.printout(("%s %s depends on %s (%s)"):format(
|
||||
rockspec.name, rockspec.version, deps.show_dep(dep), rock_status(dep.name, deps_mode)))
|
||||
|
||||
if dep.constraints[1] and dep.constraints[1].no_upgrade then
|
||||
util.printerr("This version of "..rockspec.name.." is designed for use with")
|
||||
util.printerr(deps.show_dep(dep)..", but is configured to avoid upgrading it")
|
||||
util.printerr("automatically. Please upgrade "..dep.name.." with")
|
||||
util.printerr(" luarocks install "..dep.name)
|
||||
util.printerr("or choose an older version of "..rockspec.name.." with")
|
||||
util.printerr(" luarocks search "..rockspec.name)
|
||||
return nil, "Failed matching dependencies"
|
||||
end
|
||||
|
||||
local url, search_err = search.find_suitable_rock(dep)
|
||||
if not url then
|
||||
return nil, "Could not satisfy dependency "..deps.show_dep(dep)..": "..search_err
|
||||
end
|
||||
util.printout("Installing "..url)
|
||||
local ok, install_err, errcode = install.command({deps_mode = deps_mode}, url)
|
||||
if not ok then
|
||||
return nil, "Failed installing dependency: "..url.." - "..install_err, errcode
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- If filename matches a pattern, return the capture.
|
||||
-- For example, given "libfoo.so" and "lib?.so" is a pattern,
|
||||
-- returns "foo" (which can then be used to build names
|
||||
-- based on other patterns.
|
||||
-- @param file string: a filename
|
||||
-- @param pattern string: a pattern, where ? is to be matched by the filename.
|
||||
-- @return string The pattern, if found, or nil.
|
||||
local function deconstruct_pattern(file, pattern)
|
||||
local depattern = "^"..(pattern:gsub("%.", "%%."):gsub("%*", ".*"):gsub("?", "(.*)")).."$"
|
||||
return (file:match(depattern))
|
||||
end
|
||||
|
||||
--- Construct all possible patterns for a name and add to the files array.
|
||||
-- Run through the patterns array replacing all occurrences of "?"
|
||||
-- with the given file name and store them in the files array.
|
||||
-- @param file string A raw name (e.g. "foo")
|
||||
-- @param array of string An array of patterns with "?" as the wildcard
|
||||
-- (e.g. {"?.so", "lib?.so"})
|
||||
-- @param files The array of constructed names
|
||||
local function add_all_patterns(file, patterns, files)
|
||||
for _, pattern in ipairs(patterns) do
|
||||
table.insert(files, (pattern:gsub("?", file)))
|
||||
end
|
||||
end
|
||||
|
||||
--- Set up path-related variables for external dependencies.
|
||||
-- For each key in the external_dependencies table in the
|
||||
-- rockspec file, four variables are created: <key>_DIR, <key>_BINDIR,
|
||||
-- <key>_INCDIR and <key>_LIBDIR. These are not overwritten
|
||||
-- if already set (e.g. by the LuaRocks config file or through the
|
||||
-- command-line). Values in the external_dependencies table
|
||||
-- are tables that may contain a "header" or a "library" field,
|
||||
-- with filenames to be tested for existence.
|
||||
-- @param rockspec table: The rockspec table.
|
||||
-- @param mode string: if "build" is given, checks all files;
|
||||
-- if "install" is given, do not scan for headers.
|
||||
-- @return boolean or (nil, string): True if no errors occurred, or
|
||||
-- nil and an error message if any test failed.
|
||||
function deps.check_external_deps(rockspec, mode)
|
||||
assert(type(rockspec) == "table")
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
|
||||
local vars = rockspec.variables
|
||||
local patterns = cfg.external_deps_patterns
|
||||
local subdirs = cfg.external_deps_subdirs
|
||||
if mode == "install" then
|
||||
patterns = cfg.runtime_external_deps_patterns
|
||||
subdirs = cfg.runtime_external_deps_subdirs
|
||||
end
|
||||
if rockspec.external_dependencies then
|
||||
for name, ext_files in util.sortedpairs(rockspec.external_dependencies) do
|
||||
local ok = true
|
||||
local failed_files = {program = {}, header = {}, library = {}}
|
||||
local failed_dirname
|
||||
local failed_testfile
|
||||
for _, extdir in ipairs(cfg.external_deps_dirs) do
|
||||
ok = true
|
||||
local prefix = vars[name.."_DIR"]
|
||||
local dirs = {
|
||||
BINDIR = { subdir = subdirs.bin, testfile = "program", pattern = patterns.bin },
|
||||
INCDIR = { subdir = subdirs.include, testfile = "header", pattern = patterns.include },
|
||||
LIBDIR = { subdir = subdirs.lib, testfile = "library", pattern = patterns.lib }
|
||||
}
|
||||
if mode == "install" then
|
||||
dirs.INCDIR = nil
|
||||
end
|
||||
if not prefix then
|
||||
prefix = extdir
|
||||
end
|
||||
if type(prefix) == "table" then
|
||||
if prefix.bin then
|
||||
dirs.BINDIR.subdir = prefix.bin
|
||||
end
|
||||
if prefix.include then
|
||||
if dirs.INCDIR then
|
||||
dirs.INCDIR.subdir = prefix.include
|
||||
end
|
||||
end
|
||||
if prefix.lib then
|
||||
dirs.LIBDIR.subdir = prefix.lib
|
||||
end
|
||||
prefix = prefix.prefix
|
||||
end
|
||||
for dirname, dirdata in util.sortedpairs(dirs) do
|
||||
local paths
|
||||
local path_var_value = vars[name.."_"..dirname]
|
||||
if path_var_value then
|
||||
paths = { path_var_value }
|
||||
elseif type(dirdata.subdir) == "table" then
|
||||
paths = {}
|
||||
for i,v in ipairs(dirdata.subdir) do
|
||||
paths[i] = dir.path(prefix, v)
|
||||
end
|
||||
else
|
||||
paths = { dir.path(prefix, dirdata.subdir) }
|
||||
end
|
||||
dirdata.dir = paths[1]
|
||||
local file = ext_files[dirdata.testfile]
|
||||
if file then
|
||||
local files = {}
|
||||
if not file:match("%.") then
|
||||
add_all_patterns(file, dirdata.pattern, files)
|
||||
else
|
||||
for _, pattern in ipairs(dirdata.pattern) do
|
||||
local matched = deconstruct_pattern(file, pattern)
|
||||
if matched then
|
||||
add_all_patterns(matched, dirdata.pattern, files)
|
||||
end
|
||||
end
|
||||
table.insert(files, file)
|
||||
end
|
||||
local found = false
|
||||
for _, f in ipairs(files) do
|
||||
|
||||
-- small convenience hack
|
||||
if f:match("%.so$") or f:match("%.dylib$") or f:match("%.dll$") then
|
||||
f = f:gsub("%.[^.]+$", "."..cfg.external_lib_extension)
|
||||
end
|
||||
|
||||
local pattern
|
||||
if f:match("%*") then
|
||||
pattern = f:gsub("%.", "%%."):gsub("%*", ".*")
|
||||
f = "matching "..f
|
||||
end
|
||||
|
||||
for _, d in ipairs(paths) do
|
||||
if pattern then
|
||||
for entry in fs.dir(d) do
|
||||
if entry:match(pattern) then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
found = fs.is_file(dir.path(d, f))
|
||||
end
|
||||
if found then
|
||||
dirdata.dir = d
|
||||
break
|
||||
else
|
||||
table.insert(failed_files[dirdata.testfile], f.." in "..d)
|
||||
end
|
||||
end
|
||||
if found then
|
||||
break
|
||||
end
|
||||
end
|
||||
if not found then
|
||||
ok = false
|
||||
failed_dirname = dirname
|
||||
failed_testfile = dirdata.testfile
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if ok then
|
||||
for dirname, dirdata in pairs(dirs) do
|
||||
vars[name.."_"..dirname] = dirdata.dir
|
||||
end
|
||||
vars[name.."_DIR"] = prefix
|
||||
break
|
||||
end
|
||||
end
|
||||
if not ok then
|
||||
local lines = {"Could not find "..failed_testfile.." file for "..name}
|
||||
|
||||
local failed_paths = {}
|
||||
for _, failed_file in ipairs(failed_files[failed_testfile]) do
|
||||
if not failed_paths[failed_file] then
|
||||
failed_paths[failed_file] = true
|
||||
table.insert(lines, " No file "..failed_file)
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, "You may have to install "..name.." in your system and/or pass "..name.."_DIR or "..name.."_"..failed_dirname.." to the luarocks command.")
|
||||
table.insert(lines, "Example: luarocks install "..rockspec.name.." "..name.."_DIR=/usr/local")
|
||||
|
||||
return nil, table.concat(lines, "\n"), "dependency"
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Recursively add satisfied dependencies of a package to a table,
|
||||
-- to build a transitive closure of all dependent packages.
|
||||
-- Additionally ensures that `dependencies` table of the manifest is up-to-date.
|
||||
-- @param results table: The results table being built, maps package names to versions.
|
||||
-- @param manifest table: The manifest table containing dependencies.
|
||||
-- @param name string: Package name.
|
||||
-- @param version string: Package version.
|
||||
function deps.scan_deps(results, manifest, name, version, deps_mode)
|
||||
assert(type(results) == "table")
|
||||
assert(type(manifest) == "table")
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
|
||||
local fetch = require("luarocks.fetch")
|
||||
|
||||
if results[name] then
|
||||
return
|
||||
end
|
||||
if not manifest.dependencies then manifest.dependencies = {} end
|
||||
local dependencies = manifest.dependencies
|
||||
if not dependencies[name] then dependencies[name] = {} end
|
||||
local dependencies_name = dependencies[name]
|
||||
local deplist = dependencies_name[version]
|
||||
local rockspec, err
|
||||
if not deplist then
|
||||
rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version), false)
|
||||
if not rockspec then
|
||||
util.printerr("Couldn't load rockspec for "..name.." "..version..": "..err)
|
||||
return
|
||||
end
|
||||
dependencies_name[version] = rockspec.dependencies
|
||||
else
|
||||
rockspec = { dependencies = deplist }
|
||||
end
|
||||
local matched = deps.match_deps(rockspec, nil, deps_mode)
|
||||
results[name] = version
|
||||
for _, match in pairs(matched) do
|
||||
deps.scan_deps(results, manifest, match.name, match.version, deps_mode)
|
||||
end
|
||||
end
|
||||
|
||||
local valid_deps_modes = {
|
||||
one = true,
|
||||
order = true,
|
||||
all = true,
|
||||
none = true,
|
||||
}
|
||||
|
||||
function deps.check_deps_mode_flag(flag)
|
||||
return valid_deps_modes[flag]
|
||||
end
|
||||
|
||||
function deps.get_deps_mode(flags)
|
||||
if flags["deps-mode"] then
|
||||
return flags["deps-mode"]
|
||||
else
|
||||
return cfg.deps_mode
|
||||
end
|
||||
end
|
||||
|
||||
function deps.deps_mode_to_flag(deps_mode)
|
||||
return "--deps-mode="..deps_mode
|
||||
end
|
||||
|
||||
return deps
|
||||
74
Utils/luarocks/lua/luarocks/dir.lua
Normal file
74
Utils/luarocks/lua/luarocks/dir.lua
Normal file
@ -0,0 +1,74 @@
|
||||
|
||||
--- Generic utilities for handling pathnames.
|
||||
local dir = {}
|
||||
package.loaded["luarocks.dir"] = dir
|
||||
|
||||
dir.separator = "/"
|
||||
|
||||
--- Strip the path off a path+filename.
|
||||
-- @param pathname string: A path+name, such as "/a/b/c"
|
||||
-- or "\a\b\c".
|
||||
-- @return string: The filename without its path, such as "c".
|
||||
function dir.base_name(pathname)
|
||||
assert(type(pathname) == "string")
|
||||
|
||||
local base = pathname:gsub("[/\\]*$", ""):match(".*[/\\]([^/\\]*)")
|
||||
return base or pathname
|
||||
end
|
||||
|
||||
--- Strip the name off a path+filename.
|
||||
-- @param pathname string: A path+name, such as "/a/b/c".
|
||||
-- @return string: The filename without its path, such as "/a/b".
|
||||
-- For entries such as "/a/b/", "/a" is returned. If there are
|
||||
-- no directory separators in input, "" is returned.
|
||||
function dir.dir_name(pathname)
|
||||
assert(type(pathname) == "string")
|
||||
return (pathname:gsub("/*$", ""):match("(.*)[/]+[^/]*")) or ""
|
||||
end
|
||||
|
||||
--- Describe a path in a cross-platform way.
|
||||
-- Use this function to avoid platform-specific directory
|
||||
-- separators in other modules. Removes trailing slashes from
|
||||
-- each component given, to avoid repeated separators.
|
||||
-- Separators inside strings are kept, to handle URLs containing
|
||||
-- protocols.
|
||||
-- @param ... strings representing directories
|
||||
-- @return string: a string with a platform-specific representation
|
||||
-- of the path.
|
||||
function dir.path(...)
|
||||
local t = {...}
|
||||
while t[1] == "" do
|
||||
table.remove(t, 1)
|
||||
end
|
||||
return (table.concat(t, "/"):gsub("([^:])/+", "%1/"):gsub("^/+", "/"):gsub("/*$", ""))
|
||||
end
|
||||
|
||||
--- Split protocol and path from an URL or local pathname.
|
||||
-- URLs should be in the "protocol://path" format.
|
||||
-- For local pathnames, "file" is returned as the protocol.
|
||||
-- @param url string: an URL or a local pathname.
|
||||
-- @return string, string: the protocol, and the pathname without the protocol.
|
||||
function dir.split_url(url)
|
||||
assert(type(url) == "string")
|
||||
|
||||
local protocol, pathname = url:match("^([^:]*)://(.*)")
|
||||
if not protocol then
|
||||
protocol = "file"
|
||||
pathname = url
|
||||
end
|
||||
return protocol, pathname
|
||||
end
|
||||
|
||||
--- Normalize a url or local path.
|
||||
-- URLs should be in the "protocol://path" format. System independent
|
||||
-- forward slashes are used, removing trailing and double slashes
|
||||
-- @param url string: an URL or a local pathname.
|
||||
-- @return string: Normalized result.
|
||||
function dir.normalize(name)
|
||||
local protocol, pathname = dir.split_url(name)
|
||||
pathname = pathname:gsub("\\", "/"):gsub("(.)/*$", "%1"):gsub("//", "/")
|
||||
if protocol ~= "file" then pathname = protocol .."://"..pathname end
|
||||
return pathname
|
||||
end
|
||||
|
||||
return dir
|
||||
157
Utils/luarocks/lua/luarocks/doc.lua
Normal file
157
Utils/luarocks/lua/luarocks/doc.lua
Normal file
@ -0,0 +1,157 @@
|
||||
|
||||
--- Module implementing the LuaRocks "doc" command.
|
||||
-- Shows documentation for an installed rock.
|
||||
local doc = {}
|
||||
package.loaded["luarocks.doc"] = doc
|
||||
|
||||
local util = require("luarocks.util")
|
||||
local search = require("luarocks.search")
|
||||
local path = require("luarocks.path")
|
||||
local dir = require("luarocks.dir")
|
||||
local fetch = require("luarocks.fetch")
|
||||
local fs = require("luarocks.fs")
|
||||
local download = require("luarocks.download")
|
||||
|
||||
util.add_run_function(doc)
|
||||
doc.help_summary = "Show documentation for an installed rock."
|
||||
|
||||
doc.help = [[
|
||||
<argument> is an existing package name.
|
||||
Without any flags, tries to load the documentation
|
||||
using a series of heuristics.
|
||||
With these flags, return only the desired information:
|
||||
|
||||
--home Open the home page of project.
|
||||
--list List documentation files only.
|
||||
|
||||
For more information about a rock, see the 'show' command.
|
||||
]]
|
||||
|
||||
local function show_homepage(homepage, name, version)
|
||||
if not homepage then
|
||||
return nil, "No 'homepage' field in rockspec for "..name.." "..version
|
||||
end
|
||||
util.printout("Opening "..homepage.." ...")
|
||||
fs.browser(homepage)
|
||||
return true
|
||||
end
|
||||
|
||||
local function try_to_open_homepage(name, version)
|
||||
local temp_dir, err = fs.make_temp_dir("doc-"..name.."-"..(version or ""))
|
||||
if not temp_dir then
|
||||
return nil, "Failed creating temporary directory: "..err
|
||||
end
|
||||
util.schedule_function(fs.delete, temp_dir)
|
||||
local ok, err = fs.change_dir(temp_dir)
|
||||
if not ok then return nil, err end
|
||||
local filename, err = download.download("rockspec", name, version)
|
||||
if not filename then return nil, err end
|
||||
local rockspec, err = fetch.load_local_rockspec(filename)
|
||||
if not rockspec then return nil, err end
|
||||
fs.pop_dir()
|
||||
local descript = rockspec.description or {}
|
||||
if not descript.homepage then return nil, "No homepage defined for "..name end
|
||||
return show_homepage(descript.homepage, name, version)
|
||||
end
|
||||
|
||||
--- Driver function for "doc" command.
|
||||
-- @param name or nil: an existing package name.
|
||||
-- @param version string or nil: a version may also be passed.
|
||||
-- @return boolean: True if succeeded, nil on errors.
|
||||
function doc.command(flags, name, version)
|
||||
if not name then
|
||||
return nil, "Argument missing. "..util.see_help("doc")
|
||||
end
|
||||
|
||||
name = name:lower()
|
||||
|
||||
local iname, iversion, repo = search.pick_installed_rock(name, version, flags["tree"])
|
||||
if not iname then
|
||||
util.printout(name..(version and " "..version or "").." is not installed. Looking for it in the rocks servers...")
|
||||
return try_to_open_homepage(name, version)
|
||||
end
|
||||
name, version = iname, iversion
|
||||
|
||||
local rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version, repo))
|
||||
if not rockspec then return nil,err end
|
||||
local descript = rockspec.description or {}
|
||||
|
||||
if flags["home"] then
|
||||
return show_homepage(descript.homepage, name, version)
|
||||
end
|
||||
|
||||
local directory = path.install_dir(name,version,repo)
|
||||
|
||||
local docdir
|
||||
local directories = { "doc", "docs" }
|
||||
for _, d in ipairs(directories) do
|
||||
local dirname = dir.path(directory, d)
|
||||
if fs.is_dir(dirname) then
|
||||
docdir = dirname
|
||||
break
|
||||
end
|
||||
end
|
||||
if not docdir then
|
||||
if descript.homepage and not flags["list"] then
|
||||
util.printout("Local documentation directory not found -- opening "..descript.homepage.." ...")
|
||||
fs.browser(descript.homepage)
|
||||
return true
|
||||
end
|
||||
return nil, "Documentation directory not found for "..name.." "..version
|
||||
end
|
||||
|
||||
docdir = dir.normalize(docdir):gsub("/+", "/")
|
||||
local files = fs.find(docdir)
|
||||
local htmlpatt = "%.html?$"
|
||||
local extensions = { htmlpatt, "%.md$", "%.txt$", "%.textile$", "" }
|
||||
local basenames = { "index", "readme", "manual" }
|
||||
|
||||
local porcelain = flags["porcelain"]
|
||||
if #files > 0 then
|
||||
util.title("Documentation files for "..name.." "..version, porcelain)
|
||||
if porcelain then
|
||||
for _, file in ipairs(files) do
|
||||
util.printout(docdir.."/"..file)
|
||||
end
|
||||
else
|
||||
util.printout(docdir.."/")
|
||||
for _, file in ipairs(files) do
|
||||
util.printout("\t"..file)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if flags["list"] then
|
||||
return true
|
||||
end
|
||||
|
||||
for _, extension in ipairs(extensions) do
|
||||
for _, basename in ipairs(basenames) do
|
||||
local filename = basename..extension
|
||||
local found
|
||||
for _, file in ipairs(files) do
|
||||
if file:lower():match(filename) and ((not found) or #file < #found) then
|
||||
found = file
|
||||
end
|
||||
end
|
||||
if found then
|
||||
local pathname = dir.path(docdir, found)
|
||||
util.printout()
|
||||
util.printout("Opening "..pathname.." ...")
|
||||
util.printout()
|
||||
local ok = fs.browser(pathname)
|
||||
if not ok and not pathname:match(htmlpatt) then
|
||||
local fd = io.open(pathname, "r")
|
||||
util.printout(fd:read("*a"))
|
||||
fd:close()
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
return doc
|
||||
109
Utils/luarocks/lua/luarocks/download.lua
Normal file
109
Utils/luarocks/lua/luarocks/download.lua
Normal file
@ -0,0 +1,109 @@
|
||||
|
||||
--- Module implementing the luarocks "download" command.
|
||||
-- Download a rock from the repository.
|
||||
local download = {}
|
||||
package.loaded["luarocks.download"] = download
|
||||
|
||||
local util = require("luarocks.util")
|
||||
local path = require("luarocks.path")
|
||||
local fetch = require("luarocks.fetch")
|
||||
local search = require("luarocks.search")
|
||||
local fs = require("luarocks.fs")
|
||||
local dir = require("luarocks.dir")
|
||||
local cfg = require("luarocks.cfg")
|
||||
|
||||
util.add_run_function(download)
|
||||
download.help_summary = "Download a specific rock file from a rocks server."
|
||||
download.help_arguments = "[--all] [--arch=<arch> | --source | --rockspec] [<name> [<version>]]"
|
||||
|
||||
download.help = [[
|
||||
--all Download all files if there are multiple matches.
|
||||
--source Download .src.rock if available.
|
||||
--rockspec Download .rockspec if available.
|
||||
--arch=<arch> Download rock for a specific architecture.
|
||||
]]
|
||||
|
||||
local function get_file(filename)
|
||||
local protocol, pathname = dir.split_url(filename)
|
||||
if protocol == "file" then
|
||||
local ok, err = fs.copy(pathname, fs.current_dir(), cfg.perm_read)
|
||||
if ok then
|
||||
return pathname
|
||||
else
|
||||
return nil, err
|
||||
end
|
||||
else
|
||||
return fetch.fetch_url(filename)
|
||||
end
|
||||
end
|
||||
|
||||
function download.download(arch, name, version, all)
|
||||
local query = search.make_query(name, version)
|
||||
if arch then query.arch = arch end
|
||||
local search_err
|
||||
|
||||
if all then
|
||||
if name == "" then query.exact_name = false end
|
||||
local results = search.search_repos(query)
|
||||
local has_result = false
|
||||
local all_ok = true
|
||||
local any_err = ""
|
||||
for name, result in pairs(results) do
|
||||
for version, items in pairs(result) do
|
||||
for _, item in ipairs(items) do
|
||||
-- Ignore provided rocks.
|
||||
if item.arch ~= "installed" then
|
||||
has_result = true
|
||||
local filename = path.make_url(item.repo, name, version, item.arch)
|
||||
local ok, err = get_file(filename)
|
||||
if not ok then
|
||||
all_ok = false
|
||||
any_err = any_err .. "\n" .. err
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if has_result then
|
||||
return all_ok, any_err
|
||||
end
|
||||
else
|
||||
local url
|
||||
url, search_err = search.find_suitable_rock(query)
|
||||
if url then
|
||||
return get_file(url)
|
||||
end
|
||||
end
|
||||
return nil, "Could not find a result named "..name..(version and " "..version or "")..
|
||||
(search_err and ": "..search_err or ".")
|
||||
end
|
||||
|
||||
--- Driver function for the "download" command.
|
||||
-- @param name string: a rock name.
|
||||
-- @param version string or nil: if the name of a package is given, a
|
||||
-- version may also be passed.
|
||||
-- @return boolean or (nil, string): true if successful or nil followed
|
||||
-- by an error message.
|
||||
function download.command(flags, name, version)
|
||||
assert(type(version) == "string" or not version)
|
||||
if type(name) ~= "string" and not flags["all"] then
|
||||
return nil, "Argument missing. "..util.see_help("download")
|
||||
end
|
||||
if not name then name, version = "", "" end
|
||||
|
||||
local arch
|
||||
|
||||
if flags["source"] then
|
||||
arch = "src"
|
||||
elseif flags["rockspec"] then
|
||||
arch = "rockspec"
|
||||
elseif flags["arch"] then
|
||||
arch = flags["arch"]
|
||||
end
|
||||
|
||||
local dl, err = download.download(arch, name:lower(), version, flags["all"])
|
||||
return dl and true, err
|
||||
end
|
||||
|
||||
return download
|
||||
394
Utils/luarocks/lua/luarocks/fetch.lua
Normal file
394
Utils/luarocks/lua/luarocks/fetch.lua
Normal file
@ -0,0 +1,394 @@
|
||||
|
||||
--- Functions related to fetching and loading local and remote files.
|
||||
local fetch = {}
|
||||
package.loaded["luarocks.fetch"] = fetch
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
local dir = require("luarocks.dir")
|
||||
local type_check = require("luarocks.type_check")
|
||||
local path = require("luarocks.path")
|
||||
local deps = require("luarocks.deps")
|
||||
local persist = require("luarocks.persist")
|
||||
local util = require("luarocks.util")
|
||||
local cfg = require("luarocks.cfg")
|
||||
|
||||
function fetch.is_basic_protocol(protocol, remote)
|
||||
return protocol == "http" or protocol == "https" or protocol == "ftp" or (not remote and protocol == "file")
|
||||
end
|
||||
|
||||
--- Fetch a local or remote file.
|
||||
-- Make a remote or local URL/pathname local, fetching the file if necessary.
|
||||
-- Other "fetch" and "load" functions use this function to obtain files.
|
||||
-- If a local pathname is given, it is returned as a result.
|
||||
-- @param url string: a local pathname or a remote URL.
|
||||
-- @param filename string or nil: this function attempts to detect the
|
||||
-- resulting local filename of the remote file as the basename of the URL;
|
||||
-- if that is not correct (due to a redirection, for example), the local
|
||||
-- filename can be given explicitly as this second argument.
|
||||
-- @return string or (nil, string, [string]): the absolute local pathname for the
|
||||
-- fetched file, or nil and a message in case of errors, followed by
|
||||
-- an optional error code.
|
||||
function fetch.fetch_url(url, filename, cache)
|
||||
assert(type(url) == "string")
|
||||
assert(type(filename) == "string" or not filename)
|
||||
|
||||
local protocol, pathname = dir.split_url(url)
|
||||
if protocol == "file" then
|
||||
return fs.absolute_name(pathname)
|
||||
elseif fetch.is_basic_protocol(protocol, true) then
|
||||
local ok, name = fs.download(url, filename, cache)
|
||||
if not ok then
|
||||
return nil, "Failed downloading "..url..(filename and " - "..filename or ""), "network"
|
||||
end
|
||||
return name
|
||||
else
|
||||
return nil, "Unsupported protocol "..protocol
|
||||
end
|
||||
end
|
||||
|
||||
--- For remote URLs, create a temporary directory and download URL inside it.
|
||||
-- This temporary directory will be deleted on program termination.
|
||||
-- For local URLs, just return the local pathname and its directory.
|
||||
-- @param url string: URL to be downloaded
|
||||
-- @param tmpname string: name pattern to use for avoiding conflicts
|
||||
-- when creating temporary directory.
|
||||
-- @param filename string or nil: local filename of URL to be downloaded,
|
||||
-- in case it can't be inferred from the URL.
|
||||
-- @return (string, string) or (nil, string, [string]): absolute local pathname of
|
||||
-- the fetched file and temporary directory name; or nil and an error message
|
||||
-- followed by an optional error code
|
||||
function fetch.fetch_url_at_temp_dir(url, tmpname, filename)
|
||||
assert(type(url) == "string")
|
||||
assert(type(tmpname) == "string")
|
||||
assert(type(filename) == "string" or not filename)
|
||||
filename = filename or dir.base_name(url)
|
||||
|
||||
local protocol, pathname = dir.split_url(url)
|
||||
if protocol == "file" then
|
||||
if fs.exists(pathname) then
|
||||
return pathname, dir.dir_name(fs.absolute_name(pathname))
|
||||
else
|
||||
return nil, "File not found: "..pathname
|
||||
end
|
||||
else
|
||||
local temp_dir, err = fs.make_temp_dir(tmpname)
|
||||
if not temp_dir then
|
||||
return nil, "Failed creating temporary directory "..tmpname..": "..err
|
||||
end
|
||||
util.schedule_function(fs.delete, temp_dir)
|
||||
local ok, err = fs.change_dir(temp_dir)
|
||||
if not ok then return nil, err end
|
||||
local file, err, errcode = fetch.fetch_url(url, filename)
|
||||
fs.pop_dir()
|
||||
if not file then
|
||||
return nil, "Error fetching file: "..err, errcode
|
||||
end
|
||||
return file, temp_dir
|
||||
end
|
||||
end
|
||||
|
||||
-- Determine base directory of a fetched URL by extracting its
|
||||
-- archive and looking for a directory in the root.
|
||||
-- @param file string: absolute local pathname of the fetched file
|
||||
-- @param temp_dir string: temporary directory in which URL was fetched.
|
||||
-- @param src_url string: URL to use when inferring base directory.
|
||||
-- @param src_dir string or nil: expected base directory (inferred
|
||||
-- from src_url if not given).
|
||||
-- @return (string, string) or (string, nil) or (nil, string):
|
||||
-- The inferred base directory and the one actually found (which may
|
||||
-- be nil if not found), or nil followed by an error message.
|
||||
-- The inferred dir is returned first to avoid confusion with errors,
|
||||
-- because it is never nil.
|
||||
function fetch.find_base_dir(file, temp_dir, src_url, src_dir)
|
||||
local ok, err = fs.change_dir(temp_dir)
|
||||
if not ok then return nil, err end
|
||||
fs.unpack_archive(file)
|
||||
local inferred_dir = src_dir or fetch.url_to_base_dir(src_url)
|
||||
local found_dir = nil
|
||||
if fs.exists(inferred_dir) then
|
||||
found_dir = inferred_dir
|
||||
else
|
||||
util.printerr("Directory "..inferred_dir.." not found")
|
||||
local files = fs.list_dir()
|
||||
if files then
|
||||
table.sort(files)
|
||||
for i,filename in ipairs(files) do
|
||||
if fs.is_dir(filename) then
|
||||
util.printerr("Found "..filename)
|
||||
found_dir = filename
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
fs.pop_dir()
|
||||
return inferred_dir, found_dir
|
||||
end
|
||||
|
||||
--- Obtain a rock and unpack it.
|
||||
-- If a directory is not given, a temporary directory will be created,
|
||||
-- which will be deleted on program termination.
|
||||
-- @param rock_file string: URL or filename of the rock.
|
||||
-- @param dest string or nil: if given, directory will be used as
|
||||
-- a permanent destination.
|
||||
-- @return string or (nil, string, [string]): the directory containing the contents
|
||||
-- of the unpacked rock.
|
||||
function fetch.fetch_and_unpack_rock(rock_file, dest)
|
||||
assert(type(rock_file) == "string")
|
||||
assert(type(dest) == "string" or not dest)
|
||||
|
||||
local name = dir.base_name(rock_file):match("(.*)%.[^.]*%.rock")
|
||||
|
||||
local rock_file, err, errcode = fetch.fetch_url_at_temp_dir(rock_file,"luarocks-rock-"..name)
|
||||
if not rock_file then
|
||||
return nil, "Could not fetch rock file: " .. err, errcode
|
||||
end
|
||||
|
||||
rock_file = fs.absolute_name(rock_file)
|
||||
local unpack_dir
|
||||
if dest then
|
||||
unpack_dir = dest
|
||||
local ok, err = fs.make_dir(unpack_dir)
|
||||
if not ok then
|
||||
return nil, "Failed unpacking rock file: " .. err
|
||||
end
|
||||
else
|
||||
unpack_dir = fs.make_temp_dir(name)
|
||||
end
|
||||
if not dest then
|
||||
util.schedule_function(fs.delete, unpack_dir)
|
||||
end
|
||||
local ok, err = fs.change_dir(unpack_dir)
|
||||
if not ok then return nil, err end
|
||||
ok = fs.unzip(rock_file)
|
||||
if not ok then
|
||||
return nil, "Failed unpacking rock file: " .. rock_file
|
||||
end
|
||||
fs.pop_dir()
|
||||
return unpack_dir
|
||||
end
|
||||
|
||||
function fetch.url_to_base_dir(url)
|
||||
-- for extensions like foo.tar.gz, "gz" is stripped first
|
||||
local known_exts = {}
|
||||
for _, ext in ipairs{"zip", "git", "tgz", "tar", "gz", "bz2"} do
|
||||
known_exts[ext] = ""
|
||||
end
|
||||
local base = dir.base_name(url)
|
||||
return (base:gsub("%.([^.]*)$", known_exts):gsub("%.tar", ""))
|
||||
end
|
||||
|
||||
--- Back-end function that actually loads the local rockspec.
|
||||
-- Performs some validation and postprocessing of the rockspec contents.
|
||||
-- @param filename string: The local filename of the rockspec file.
|
||||
-- @param quick boolean: if true, skips some steps when loading
|
||||
-- rockspec.
|
||||
-- @return table or (nil, string): A table representing the rockspec
|
||||
-- or nil followed by an error message.
|
||||
function fetch.load_local_rockspec(filename, quick)
|
||||
assert(type(filename) == "string")
|
||||
filename = fs.absolute_name(filename)
|
||||
local rockspec, err = persist.load_into_table(filename)
|
||||
if not rockspec then
|
||||
return nil, "Could not load rockspec file "..filename.." ("..err..")"
|
||||
end
|
||||
if cfg.branch and (type(rockspec.source) == "table") then
|
||||
rockspec.source.branch = cfg.branch
|
||||
end
|
||||
local globals = err
|
||||
|
||||
if rockspec.rockspec_format then
|
||||
if deps.compare_versions(rockspec.rockspec_format, type_check.rockspec_format) then
|
||||
return nil, "Rockspec format "..rockspec.rockspec_format.." is not supported, please upgrade LuaRocks."
|
||||
end
|
||||
end
|
||||
|
||||
if not quick then
|
||||
local ok, err = type_check.type_check_rockspec(rockspec, globals)
|
||||
if not ok then
|
||||
return nil, filename..": "..err
|
||||
end
|
||||
end
|
||||
|
||||
util.platform_overrides(rockspec.build)
|
||||
util.platform_overrides(rockspec.dependencies)
|
||||
util.platform_overrides(rockspec.external_dependencies)
|
||||
util.platform_overrides(rockspec.source)
|
||||
util.platform_overrides(rockspec.hooks)
|
||||
|
||||
local basename = dir.base_name(filename)
|
||||
if basename == "rockspec" then
|
||||
rockspec.name = rockspec.package:lower()
|
||||
else
|
||||
rockspec.name = basename:match("(.*)-[^-]*-[0-9]*")
|
||||
if not rockspec.name then
|
||||
return nil, "Expected filename in format 'name-version-revision.rockspec'."
|
||||
end
|
||||
end
|
||||
|
||||
local protocol, pathname = dir.split_url(rockspec.source.url)
|
||||
if fetch.is_basic_protocol(protocol) then
|
||||
rockspec.source.file = rockspec.source.file or dir.base_name(rockspec.source.url)
|
||||
end
|
||||
rockspec.source.protocol, rockspec.source.pathname = protocol, pathname
|
||||
|
||||
-- Temporary compatibility
|
||||
if rockspec.source.cvs_module then rockspec.source.module = rockspec.source.cvs_module end
|
||||
if rockspec.source.cvs_tag then rockspec.source.tag = rockspec.source.cvs_tag end
|
||||
|
||||
local name_version = rockspec.package:lower() .. "-" .. rockspec.version
|
||||
if basename ~= "rockspec" and basename ~= name_version .. ".rockspec" then
|
||||
return nil, "Inconsistency between rockspec filename ("..basename..") and its contents ("..name_version..".rockspec)."
|
||||
end
|
||||
|
||||
rockspec.local_filename = filename
|
||||
local filebase = rockspec.source.file or rockspec.source.url
|
||||
local base = fetch.url_to_base_dir(filebase)
|
||||
rockspec.source.dir = rockspec.source.dir
|
||||
or rockspec.source.module
|
||||
or ((filebase:match("%.lua$") or filebase:match("%.c$")) and ".")
|
||||
or base
|
||||
if rockspec.dependencies then
|
||||
for i = 1, #rockspec.dependencies do
|
||||
local parsed, err = deps.parse_dep(rockspec.dependencies[i])
|
||||
if not parsed then
|
||||
return nil, "Parse error processing dependency '"..rockspec.dependencies[i].."': "..tostring(err)
|
||||
end
|
||||
rockspec.dependencies[i] = parsed
|
||||
end
|
||||
else
|
||||
rockspec.dependencies = {}
|
||||
end
|
||||
if not quick then
|
||||
path.configure_paths(rockspec)
|
||||
end
|
||||
|
||||
return rockspec
|
||||
end
|
||||
|
||||
--- Load a local or remote rockspec into a table.
|
||||
-- This is the entry point for the LuaRocks tools.
|
||||
-- Only the LuaRocks runtime loader should use
|
||||
-- load_local_rockspec directly.
|
||||
-- @param filename string: Local or remote filename of a rockspec.
|
||||
-- @param location string or nil: Where to download. If not given,
|
||||
-- a temporary dir is created.
|
||||
-- @return table or (nil, string, [string]): A table representing the rockspec
|
||||
-- or nil followed by an error message and optional error code.
|
||||
function fetch.load_rockspec(filename, location)
|
||||
assert(type(filename) == "string")
|
||||
|
||||
local name
|
||||
local basename = dir.base_name(filename)
|
||||
if basename == "rockspec" then
|
||||
name = "rockspec"
|
||||
else
|
||||
name = basename:match("(.*)%.rockspec")
|
||||
if not name then
|
||||
return nil, "Filename '"..filename.."' does not look like a rockspec."
|
||||
end
|
||||
end
|
||||
|
||||
local err, errcode
|
||||
if location then
|
||||
local ok, err = fs.change_dir(location)
|
||||
if not ok then return nil, err end
|
||||
filename, err = fetch.fetch_url(filename)
|
||||
fs.pop_dir()
|
||||
else
|
||||
filename, err, errcode = fetch.fetch_url_at_temp_dir(filename,"luarocks-rockspec-"..name)
|
||||
end
|
||||
if not filename then
|
||||
return nil, err, errcode
|
||||
end
|
||||
|
||||
return fetch.load_local_rockspec(filename)
|
||||
end
|
||||
|
||||
--- Download sources for building a rock using the basic URL downloader.
|
||||
-- @param rockspec table: The rockspec table
|
||||
-- @param extract boolean: Whether to extract the sources from
|
||||
-- the fetched source tarball or not.
|
||||
-- @param dest_dir string or nil: If set, will extract to the given directory;
|
||||
-- if not given, will extract to a temporary directory.
|
||||
-- @return (string, string) or (nil, string, [string]): The absolute pathname of
|
||||
-- the fetched source tarball and the temporary directory created to
|
||||
-- store it; or nil and an error message and optional error code.
|
||||
function fetch.get_sources(rockspec, extract, dest_dir)
|
||||
assert(type(rockspec) == "table")
|
||||
assert(type(extract) == "boolean")
|
||||
assert(type(dest_dir) == "string" or not dest_dir)
|
||||
|
||||
local url = rockspec.source.url
|
||||
local name = rockspec.name.."-"..rockspec.version
|
||||
local filename = rockspec.source.file
|
||||
local source_file, store_dir
|
||||
local ok, err, errcode
|
||||
if dest_dir then
|
||||
ok, err = fs.change_dir(dest_dir)
|
||||
if not ok then return nil, err, "dest_dir" end
|
||||
source_file, err, errcode = fetch.fetch_url(url, filename)
|
||||
fs.pop_dir()
|
||||
store_dir = dest_dir
|
||||
else
|
||||
source_file, store_dir, errcode = fetch.fetch_url_at_temp_dir(url, "luarocks-source-"..name, filename)
|
||||
end
|
||||
if not source_file then
|
||||
return nil, err or store_dir, errcode
|
||||
end
|
||||
if rockspec.source.md5 then
|
||||
if not fs.check_md5(source_file, rockspec.source.md5) then
|
||||
return nil, "MD5 check for "..filename.." has failed.", "md5"
|
||||
end
|
||||
end
|
||||
if extract then
|
||||
local ok, err = fs.change_dir(store_dir)
|
||||
if not ok then return nil, err end
|
||||
ok, err = fs.unpack_archive(rockspec.source.file)
|
||||
if not ok then return nil, err end
|
||||
if not fs.exists(rockspec.source.dir) then
|
||||
return nil, "Directory "..rockspec.source.dir.." not found inside archive "..rockspec.source.file, "source.dir", source_file, store_dir
|
||||
end
|
||||
fs.pop_dir()
|
||||
end
|
||||
return source_file, store_dir
|
||||
end
|
||||
|
||||
--- Download sources for building a rock, calling the appropriate protocol method.
|
||||
-- @param rockspec table: The rockspec table
|
||||
-- @param extract boolean: When downloading compressed formats, whether to extract
|
||||
-- the sources from the fetched archive or not.
|
||||
-- @param dest_dir string or nil: If set, will extract to the given directory.
|
||||
-- if not given, will extract to a temporary directory.
|
||||
-- @return (string, string) or (nil, string): The absolute pathname of
|
||||
-- the fetched source tarball and the temporary directory created to
|
||||
-- store it; or nil and an error message.
|
||||
function fetch.fetch_sources(rockspec, extract, dest_dir)
|
||||
assert(type(rockspec) == "table")
|
||||
assert(type(extract) == "boolean")
|
||||
assert(type(dest_dir) == "string" or not dest_dir)
|
||||
|
||||
local protocol = rockspec.source.protocol
|
||||
local ok, proto
|
||||
if fetch.is_basic_protocol(protocol) then
|
||||
proto = fetch
|
||||
else
|
||||
ok, proto = pcall(require, "luarocks.fetch."..protocol:gsub("[+-]", "_"))
|
||||
if not ok then
|
||||
return nil, "Unknown protocol "..protocol
|
||||
end
|
||||
end
|
||||
|
||||
if cfg.only_sources_from
|
||||
and rockspec.source.pathname
|
||||
and #rockspec.source.pathname > 0 then
|
||||
if #cfg.only_sources_from == 0 then
|
||||
return nil, "Can't download "..rockspec.source.url.." -- download from remote servers disabled"
|
||||
elseif rockspec.source.pathname:find(cfg.only_sources_from, 1, true) ~= 1 then
|
||||
return nil, "Can't download "..rockspec.source.url.." -- only downloading from "..cfg.only_sources_from
|
||||
end
|
||||
end
|
||||
return proto.get_sources(rockspec, extract, dest_dir)
|
||||
end
|
||||
|
||||
return fetch
|
||||
55
Utils/luarocks/lua/luarocks/fetch/cvs.lua
Normal file
55
Utils/luarocks/lua/luarocks/fetch/cvs.lua
Normal file
@ -0,0 +1,55 @@
|
||||
|
||||
--- Fetch back-end for retrieving sources from CVS.
|
||||
local cvs = {}
|
||||
|
||||
local unpack = unpack or table.unpack
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
local dir = require("luarocks.dir")
|
||||
local util = require("luarocks.util")
|
||||
|
||||
--- Download sources for building a rock, using CVS.
|
||||
-- @param rockspec table: The rockspec table
|
||||
-- @param extract boolean: Unused in this module (required for API purposes.)
|
||||
-- @param dest_dir string or nil: If set, will extract to the given directory.
|
||||
-- @return (string, string) or (nil, string): The absolute pathname of
|
||||
-- the fetched source tarball and the temporary directory created to
|
||||
-- store it; or nil and an error message.
|
||||
function cvs.get_sources(rockspec, extract, dest_dir)
|
||||
assert(type(rockspec) == "table")
|
||||
assert(type(dest_dir) == "string" or not dest_dir)
|
||||
|
||||
local cvs_cmd = rockspec.variables.CVS
|
||||
local ok, err_msg = fs.is_tool_available(cvs_cmd, "CVS")
|
||||
if not ok then
|
||||
return nil, err_msg
|
||||
end
|
||||
|
||||
local name_version = rockspec.name .. "-" .. rockspec.version
|
||||
local module = rockspec.source.module or dir.base_name(rockspec.source.url)
|
||||
local command = {cvs_cmd, "-d"..rockspec.source.pathname, "export", module}
|
||||
if rockspec.source.tag then
|
||||
table.insert(command, 4, "-r")
|
||||
table.insert(command, 5, rockspec.source.tag)
|
||||
end
|
||||
local store_dir
|
||||
if not dest_dir then
|
||||
store_dir = fs.make_temp_dir(name_version)
|
||||
if not store_dir then
|
||||
return nil, "Failed creating temporary directory."
|
||||
end
|
||||
util.schedule_function(fs.delete, store_dir)
|
||||
else
|
||||
store_dir = dest_dir
|
||||
end
|
||||
local ok, err = fs.change_dir(store_dir)
|
||||
if not ok then return nil, err end
|
||||
if not fs.execute(unpack(command)) then
|
||||
return nil, "Failed fetching files from CVS."
|
||||
end
|
||||
fs.pop_dir()
|
||||
return module, store_dir
|
||||
end
|
||||
|
||||
|
||||
return cvs
|
||||
92
Utils/luarocks/lua/luarocks/fetch/git.lua
Normal file
92
Utils/luarocks/lua/luarocks/fetch/git.lua
Normal file
@ -0,0 +1,92 @@
|
||||
|
||||
--- Fetch back-end for retrieving sources from GIT.
|
||||
local git = {}
|
||||
|
||||
local unpack = unpack or table.unpack
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
local dir = require("luarocks.dir")
|
||||
local util = require("luarocks.util")
|
||||
|
||||
--- Git >= 1.7.10 can clone a branch **or tag**, < 1.7.10 by branch only. We
|
||||
-- need to know this in order to build the appropriate command; if we can't
|
||||
-- clone by tag then we'll have to issue a subsequent command to check out the
|
||||
-- given tag.
|
||||
-- @return boolean: Whether Git can clone by tag.
|
||||
local function git_can_clone_by_tag(git_cmd)
|
||||
local version_string = io.popen(fs.Q(git_cmd)..' --version'):read()
|
||||
local major, minor, tiny = version_string:match('(%d-)%.(%d+)%.?(%d*)')
|
||||
major, minor, tiny = tonumber(major), tonumber(minor), tonumber(tiny) or 0
|
||||
local value = major > 1 or (major == 1 and (minor > 7 or (minor == 7 and tiny >= 10)))
|
||||
git_can_clone_by_tag = function() return value end
|
||||
return value
|
||||
end
|
||||
|
||||
--- Download sources for building a rock, using git.
|
||||
-- @param rockspec table: The rockspec table
|
||||
-- @param extract boolean: Unused in this module (required for API purposes.)
|
||||
-- @param dest_dir string or nil: If set, will extract to the given directory.
|
||||
-- @return (string, string) or (nil, string): The absolute pathname of
|
||||
-- the fetched source tarball and the temporary directory created to
|
||||
-- store it; or nil and an error message.
|
||||
function git.get_sources(rockspec, extract, dest_dir, depth)
|
||||
assert(type(rockspec) == "table")
|
||||
assert(type(dest_dir) == "string" or not dest_dir)
|
||||
|
||||
local git_cmd = rockspec.variables.GIT
|
||||
local name_version = rockspec.name .. "-" .. rockspec.version
|
||||
local module = dir.base_name(rockspec.source.url)
|
||||
-- Strip off .git from base name if present
|
||||
module = module:gsub("%.git$", "")
|
||||
|
||||
local ok, err_msg = fs.is_tool_available(git_cmd, "Git")
|
||||
if not ok then
|
||||
return nil, err_msg
|
||||
end
|
||||
|
||||
local store_dir
|
||||
if not dest_dir then
|
||||
store_dir = fs.make_temp_dir(name_version)
|
||||
if not store_dir then
|
||||
return nil, "Failed creating temporary directory."
|
||||
end
|
||||
util.schedule_function(fs.delete, store_dir)
|
||||
else
|
||||
store_dir = dest_dir
|
||||
end
|
||||
store_dir = fs.absolute_name(store_dir)
|
||||
local ok, err = fs.change_dir(store_dir)
|
||||
if not ok then return nil, err end
|
||||
|
||||
local command = {fs.Q(git_cmd), "clone", depth or "--depth=1", rockspec.source.url, module}
|
||||
local tag_or_branch = rockspec.source.tag or rockspec.source.branch
|
||||
-- If the tag or branch is explicitly set to "master" in the rockspec, then
|
||||
-- we can avoid passing it to Git since it's the default.
|
||||
if tag_or_branch == "master" then tag_or_branch = nil end
|
||||
if tag_or_branch then
|
||||
if git_can_clone_by_tag(git_cmd) then
|
||||
-- The argument to `--branch` can actually be a branch or a tag as of
|
||||
-- Git 1.7.10.
|
||||
table.insert(command, 3, "--branch=" .. tag_or_branch)
|
||||
end
|
||||
end
|
||||
if not fs.execute(unpack(command)) then
|
||||
return nil, "Failed cloning git repository."
|
||||
end
|
||||
ok, err = fs.change_dir(module)
|
||||
if not ok then return nil, err end
|
||||
if tag_or_branch and not git_can_clone_by_tag() then
|
||||
local checkout_command = {fs.Q(git_cmd), "checkout", tag_or_branch}
|
||||
if not fs.execute(unpack(checkout_command)) then
|
||||
return nil, 'Failed to check out the "' .. tag_or_branch ..'" tag or branch.'
|
||||
end
|
||||
end
|
||||
|
||||
fs.delete(dir.path(store_dir, module, ".git"))
|
||||
fs.delete(dir.path(store_dir, module, ".gitignore"))
|
||||
fs.pop_dir()
|
||||
fs.pop_dir()
|
||||
return module, store_dir
|
||||
end
|
||||
|
||||
return git
|
||||
19
Utils/luarocks/lua/luarocks/fetch/git_file.lua
Normal file
19
Utils/luarocks/lua/luarocks/fetch/git_file.lua
Normal file
@ -0,0 +1,19 @@
|
||||
|
||||
--- Fetch back-end for retrieving sources from local Git repositories.
|
||||
local git_file = {}
|
||||
|
||||
local git = require("luarocks.fetch.git")
|
||||
|
||||
--- Fetch sources for building a rock from a local Git repository.
|
||||
-- @param rockspec table: The rockspec table
|
||||
-- @param extract boolean: Unused in this module (required for API purposes.)
|
||||
-- @param dest_dir string or nil: If set, will extract to the given directory.
|
||||
-- @return (string, string) or (nil, string): The absolute pathname of
|
||||
-- the fetched source tarball and the temporary directory created to
|
||||
-- store it; or nil and an error message.
|
||||
function git_file.get_sources(rockspec, extract, dest_dir)
|
||||
rockspec.source.url = rockspec.source.url:gsub("^git.file://", "")
|
||||
return git.get_sources(rockspec, extract, dest_dir)
|
||||
end
|
||||
|
||||
return git_file
|
||||
26
Utils/luarocks/lua/luarocks/fetch/git_http.lua
Normal file
26
Utils/luarocks/lua/luarocks/fetch/git_http.lua
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
--- Fetch back-end for retrieving sources from Git repositories
|
||||
-- that use http:// transport. For example, for fetching a repository
|
||||
-- that requires the following command line:
|
||||
-- `git clone http://example.com/foo.git`
|
||||
-- you can use this in the rockspec:
|
||||
-- source = { url = "git+http://example.com/foo.git" }
|
||||
-- Prefer using the normal git:// fetch mode as it is more widely
|
||||
-- available in older versions of LuaRocks.
|
||||
local git_http = {}
|
||||
|
||||
local git = require("luarocks.fetch.git")
|
||||
|
||||
--- Fetch sources for building a rock from a local Git repository.
|
||||
-- @param rockspec table: The rockspec table
|
||||
-- @param extract boolean: Unused in this module (required for API purposes.)
|
||||
-- @param dest_dir string or nil: If set, will extract to the given directory.
|
||||
-- @return (string, string) or (nil, string): The absolute pathname of
|
||||
-- the fetched source tarball and the temporary directory created to
|
||||
-- store it; or nil and an error message.
|
||||
function git_http.get_sources(rockspec, extract, dest_dir)
|
||||
rockspec.source.url = rockspec.source.url:gsub("^git.", "")
|
||||
return git.get_sources(rockspec, extract, dest_dir, "--")
|
||||
end
|
||||
|
||||
return git_http
|
||||
7
Utils/luarocks/lua/luarocks/fetch/git_https.lua
Normal file
7
Utils/luarocks/lua/luarocks/fetch/git_https.lua
Normal file
@ -0,0 +1,7 @@
|
||||
--- Fetch back-end for retrieving sources from Git repositories
|
||||
-- that use https:// transport. For example, for fetching a repository
|
||||
-- that requires the following command line:
|
||||
-- `git clone https://example.com/foo.git`
|
||||
-- you can use this in the rockspec:
|
||||
-- source = { url = "git+https://example.com/foo.git" }
|
||||
return require "luarocks.fetch.git_http"
|
||||
32
Utils/luarocks/lua/luarocks/fetch/git_ssh.lua
Normal file
32
Utils/luarocks/lua/luarocks/fetch/git_ssh.lua
Normal file
@ -0,0 +1,32 @@
|
||||
--- Fetch back-end for retrieving sources from Git repositories
|
||||
-- that use ssh:// transport. For example, for fetching a repository
|
||||
-- that requires the following command line:
|
||||
-- `git clone ssh://git@example.com/path/foo.git
|
||||
-- you can use this in the rockspec:
|
||||
-- source = { url = "git+ssh://git@example.com/path/foo.git" }
|
||||
-- It also handles scp-style ssh urls: git@example.com:path/foo.git,
|
||||
-- but you have to prepend the "git+ssh://" and why not use the "newer"
|
||||
-- style anyway?
|
||||
local git_ssh = {}
|
||||
|
||||
local git = require("luarocks.fetch.git")
|
||||
|
||||
--- Fetch sources for building a rock from a local Git repository.
|
||||
-- @param rockspec table: The rockspec table
|
||||
-- @param extract boolean: Unused in this module (required for API purposes.)
|
||||
-- @param dest_dir string or nil: If set, will extract to the given directory.
|
||||
-- @return (string, string) or (nil, string): The absolute pathname of
|
||||
-- the fetched source tarball and the temporary directory created to
|
||||
-- store it; or nil and an error message.
|
||||
function git_ssh.get_sources(rockspec, extract, dest_dir)
|
||||
rockspec.source.url = rockspec.source.url:gsub("^git.", "")
|
||||
|
||||
-- Handle old-style scp-like git ssh urls
|
||||
if rockspec.source.url:match("^ssh://[^/]+:[^%d]") then
|
||||
rockspec.source.url = rockspec.source.url:gsub("^ssh://", "")
|
||||
end
|
||||
|
||||
return git.get_sources(rockspec, extract, dest_dir, "--")
|
||||
end
|
||||
|
||||
return git_ssh
|
||||
65
Utils/luarocks/lua/luarocks/fetch/hg.lua
Normal file
65
Utils/luarocks/lua/luarocks/fetch/hg.lua
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
--- Fetch back-end for retrieving sources from HG.
|
||||
local hg = {}
|
||||
|
||||
local unpack = unpack or table.unpack
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
local dir = require("luarocks.dir")
|
||||
local util = require("luarocks.util")
|
||||
|
||||
--- Download sources for building a rock, using hg.
|
||||
-- @param rockspec table: The rockspec table
|
||||
-- @param extract boolean: Unused in this module (required for API purposes.)
|
||||
-- @param dest_dir string or nil: If set, will extract to the given directory.
|
||||
-- @return (string, string) or (nil, string): The absolute pathname of
|
||||
-- the fetched source tarball and the temporary directory created to
|
||||
-- store it; or nil and an error message.
|
||||
function hg.get_sources(rockspec, extract, dest_dir)
|
||||
assert(type(rockspec) == "table")
|
||||
assert(type(dest_dir) == "string" or not dest_dir)
|
||||
|
||||
local hg_cmd = rockspec.variables.HG
|
||||
local ok, err_msg = fs.is_tool_available(hg_cmd, "Mercurial")
|
||||
if not ok then
|
||||
return nil, err_msg
|
||||
end
|
||||
|
||||
local name_version = rockspec.name .. "-" .. rockspec.version
|
||||
-- Strip off special hg:// protocol type
|
||||
local url = rockspec.source.url:gsub("^hg://", "")
|
||||
|
||||
local module = dir.base_name(url)
|
||||
|
||||
local command = {hg_cmd, "clone", url, module}
|
||||
local tag_or_branch = rockspec.source.tag or rockspec.source.branch
|
||||
if tag_or_branch then
|
||||
command = {hg_cmd, "clone", "--rev", tag_or_branch, url, module}
|
||||
end
|
||||
local store_dir
|
||||
if not dest_dir then
|
||||
store_dir = fs.make_temp_dir(name_version)
|
||||
if not store_dir then
|
||||
return nil, "Failed creating temporary directory."
|
||||
end
|
||||
util.schedule_function(fs.delete, store_dir)
|
||||
else
|
||||
store_dir = dest_dir
|
||||
end
|
||||
local ok, err = fs.change_dir(store_dir)
|
||||
if not ok then return nil, err end
|
||||
if not fs.execute(unpack(command)) then
|
||||
return nil, "Failed cloning hg repository."
|
||||
end
|
||||
ok, err = fs.change_dir(module)
|
||||
if not ok then return nil, err end
|
||||
|
||||
fs.delete(dir.path(store_dir, module, ".hg"))
|
||||
fs.delete(dir.path(store_dir, module, ".hgignore"))
|
||||
fs.pop_dir()
|
||||
fs.pop_dir()
|
||||
return module, store_dir
|
||||
end
|
||||
|
||||
|
||||
return hg
|
||||
24
Utils/luarocks/lua/luarocks/fetch/hg_http.lua
Normal file
24
Utils/luarocks/lua/luarocks/fetch/hg_http.lua
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
--- Fetch back-end for retrieving sources from hg repositories
|
||||
-- that use http:// transport. For example, for fetching a repository
|
||||
-- that requires the following command line:
|
||||
-- `hg clone http://example.com/foo`
|
||||
-- you can use this in the rockspec:
|
||||
-- source = { url = "hg+http://example.com/foo" }
|
||||
local hg_http = {}
|
||||
|
||||
local hg = require("luarocks.fetch.hg")
|
||||
|
||||
--- Download sources for building a rock, using hg over http.
|
||||
-- @param rockspec table: The rockspec table
|
||||
-- @param extract boolean: Unused in this module (required for API purposes.)
|
||||
-- @param dest_dir string or nil: If set, will extract to the given directory.
|
||||
-- @return (string, string) or (nil, string): The absolute pathname of
|
||||
-- the fetched source tarball and the temporary directory created to
|
||||
-- store it; or nil and an error message.
|
||||
function hg_http.get_sources(rockspec, extract, dest_dir)
|
||||
rockspec.source.url = rockspec.source.url:gsub("^hg.", "")
|
||||
return hg.get_sources(rockspec, extract, dest_dir)
|
||||
end
|
||||
|
||||
return hg_http
|
||||
8
Utils/luarocks/lua/luarocks/fetch/hg_https.lua
Normal file
8
Utils/luarocks/lua/luarocks/fetch/hg_https.lua
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
--- Fetch back-end for retrieving sources from hg repositories
|
||||
-- that use https:// transport. For example, for fetching a repository
|
||||
-- that requires the following command line:
|
||||
-- `hg clone https://example.com/foo`
|
||||
-- you can use this in the rockspec:
|
||||
-- source = { url = "hg+https://example.com/foo" }
|
||||
return require "luarocks.fetch.hg_http"
|
||||
8
Utils/luarocks/lua/luarocks/fetch/hg_ssh.lua
Normal file
8
Utils/luarocks/lua/luarocks/fetch/hg_ssh.lua
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
--- Fetch back-end for retrieving sources from hg repositories
|
||||
-- that use ssh:// transport. For example, for fetching a repository
|
||||
-- that requires the following command line:
|
||||
-- `hg clone ssh://example.com/foo`
|
||||
-- you can use this in the rockspec:
|
||||
-- source = { url = "hg+ssh://example.com/foo" }
|
||||
return require "luarocks.fetch.hg_http"
|
||||
44
Utils/luarocks/lua/luarocks/fetch/sscm.lua
Normal file
44
Utils/luarocks/lua/luarocks/fetch/sscm.lua
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
--- Fetch back-end for retrieving sources from Surround SCM Server
|
||||
local sscm = {}
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
local dir = require("luarocks.dir")
|
||||
|
||||
--- Download sources via Surround SCM Server for building a rock.
|
||||
-- @param rockspec table: The rockspec table
|
||||
-- @param extract boolean: Unused in this module (required for API purposes.)
|
||||
-- @param dest_dir string or nil: If set, will extract to the given directory.
|
||||
-- @return (string, string) or (nil, string): The absolute pathname of
|
||||
-- the fetched source tarball and the temporary directory created to
|
||||
-- store it; or nil and an error message.
|
||||
function sscm.get_sources(rockspec, extract, dest_dir)
|
||||
assert(type(rockspec) == "table")
|
||||
assert(type(dest_dir) == "string" or not dest_dir)
|
||||
|
||||
local sscm_cmd = rockspec.variables.SSCM
|
||||
local module = rockspec.source.module or dir.base_name(rockspec.source.url)
|
||||
local branch, repository = string.match(rockspec.source.pathname, "^([^/]*)/(.*)")
|
||||
if not branch or not repository then
|
||||
return nil, "Error retrieving branch and repository from rockspec."
|
||||
end
|
||||
-- Search for working directory.
|
||||
local working_dir
|
||||
local tmp = io.popen(string.format(sscm_cmd..[[ property "/" -d -b%s -p%s]], branch, repository))
|
||||
for line in tmp:lines() do
|
||||
--%c because a chr(13) comes in the end.
|
||||
working_dir = string.match(line, "Working directory:[%s]*(.*)%c$")
|
||||
if working_dir then break end
|
||||
end
|
||||
tmp:close()
|
||||
if not working_dir then
|
||||
return nil, "Error retrieving working directory from SSCM."
|
||||
end
|
||||
if not fs.execute(sscm_cmd, "get", "*", "-e" , "-r", "-b"..branch, "-p"..repository, "-tmodify", "-wreplace") then
|
||||
return nil, "Failed fetching files from SSCM."
|
||||
end
|
||||
-- FIXME: This function does not honor the dest_dir parameter.
|
||||
return module, working_dir
|
||||
end
|
||||
|
||||
return sscm
|
||||
64
Utils/luarocks/lua/luarocks/fetch/svn.lua
Normal file
64
Utils/luarocks/lua/luarocks/fetch/svn.lua
Normal file
@ -0,0 +1,64 @@
|
||||
|
||||
--- Fetch back-end for retrieving sources from Subversion.
|
||||
local svn = {}
|
||||
|
||||
local unpack = unpack or table.unpack
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
local dir = require("luarocks.dir")
|
||||
local util = require("luarocks.util")
|
||||
|
||||
--- Download sources for building a rock, using Subversion.
|
||||
-- @param rockspec table: The rockspec table
|
||||
-- @param extract boolean: Unused in this module (required for API purposes.)
|
||||
-- @param dest_dir string or nil: If set, will extract to the given directory.
|
||||
-- @return (string, string) or (nil, string): The absolute pathname of
|
||||
-- the fetched source tarball and the temporary directory created to
|
||||
-- store it; or nil and an error message.
|
||||
function svn.get_sources(rockspec, extract, dest_dir)
|
||||
assert(type(rockspec) == "table")
|
||||
assert(type(dest_dir) == "string" or not dest_dir)
|
||||
|
||||
local svn_cmd = rockspec.variables.SVN
|
||||
local ok, err_msg = fs.is_tool_available(svn_cmd, "--version", "Subversion")
|
||||
if not ok then
|
||||
return nil, err_msg
|
||||
end
|
||||
|
||||
local name_version = rockspec.name .. "-" .. rockspec.version
|
||||
local module = rockspec.source.module or dir.base_name(rockspec.source.url)
|
||||
local url = rockspec.source.url:gsub("^svn://", "")
|
||||
local command = {svn_cmd, "checkout", url, module}
|
||||
if rockspec.source.tag then
|
||||
table.insert(command, 5, "-r")
|
||||
table.insert(command, 6, rockspec.source.tag)
|
||||
end
|
||||
local store_dir
|
||||
if not dest_dir then
|
||||
store_dir = fs.make_temp_dir(name_version)
|
||||
if not store_dir then
|
||||
return nil, "Failed creating temporary directory."
|
||||
end
|
||||
util.schedule_function(fs.delete, store_dir)
|
||||
else
|
||||
store_dir = dest_dir
|
||||
end
|
||||
local ok, err = fs.change_dir(store_dir)
|
||||
if not ok then return nil, err end
|
||||
if not fs.execute(unpack(command)) then
|
||||
return nil, "Failed fetching files from Subversion."
|
||||
end
|
||||
ok, err = fs.change_dir(module)
|
||||
if not ok then return nil, err end
|
||||
for _, d in ipairs(fs.find(".")) do
|
||||
if dir.base_name(d) == ".svn" then
|
||||
fs.delete(dir.path(store_dir, module, d))
|
||||
end
|
||||
end
|
||||
fs.pop_dir()
|
||||
fs.pop_dir()
|
||||
return module, store_dir
|
||||
end
|
||||
|
||||
|
||||
return svn
|
||||
76
Utils/luarocks/lua/luarocks/fs.lua
Normal file
76
Utils/luarocks/lua/luarocks/fs.lua
Normal file
@ -0,0 +1,76 @@
|
||||
|
||||
--- Proxy module for filesystem and platform abstractions.
|
||||
-- All code using "fs" code should require "luarocks.fs",
|
||||
-- and not the various platform-specific implementations.
|
||||
-- However, see the documentation of the implementation
|
||||
-- for the API reference.
|
||||
|
||||
local pairs = pairs
|
||||
|
||||
local fs = {}
|
||||
package.loaded["luarocks.fs"] = fs
|
||||
|
||||
local cfg = require("luarocks.cfg")
|
||||
|
||||
local pack = table.pack or function(...) return { n = select("#", ...), ... } end
|
||||
local unpack = table.unpack or unpack
|
||||
|
||||
local old_popen, old_exec
|
||||
fs.verbose = function() -- patch io.popen and os.execute to display commands in verbose mode
|
||||
if old_popen or old_exec then return end
|
||||
old_popen = io.popen
|
||||
io.popen = function(one, two)
|
||||
if two == nil then
|
||||
print("\nio.popen: ", one)
|
||||
else
|
||||
print("\nio.popen: ", one, "Mode:", two)
|
||||
end
|
||||
return old_popen(one, two)
|
||||
end
|
||||
|
||||
old_exec = os.execute
|
||||
os.execute = function(cmd)
|
||||
-- redact api keys if present
|
||||
print("\nos.execute: ", (cmd:gsub("(/api/[^/]+/)([^/]+)/", function(cap, key) return cap.."<redacted>/" end)) )
|
||||
local code = pack(old_exec(cmd))
|
||||
print("Results: "..tostring(code.n))
|
||||
for i = 1,code.n do
|
||||
print(" "..tostring(i).." ("..type(code[i]).."): "..tostring(code[i]))
|
||||
end
|
||||
return unpack(code, 1, code.n)
|
||||
end
|
||||
end
|
||||
if cfg.verbose then fs.verbose() end
|
||||
|
||||
local function load_fns(fs_table)
|
||||
for name, fn in pairs(fs_table) do
|
||||
if not fs[name] then
|
||||
fs[name] = fn
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Load platform-specific functions
|
||||
local loaded_platform = nil
|
||||
for _, platform in ipairs(cfg.platforms) do
|
||||
local ok, fs_plat = pcall(require, "luarocks.fs."..platform)
|
||||
if ok and fs_plat then
|
||||
loaded_platform = platform
|
||||
load_fns(fs_plat)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Load platform-independent pure-Lua functionality
|
||||
local fs_lua = require("luarocks.fs.lua")
|
||||
load_fns(fs_lua)
|
||||
|
||||
-- Load platform-specific fallbacks for missing Lua modules
|
||||
local ok, fs_plat_tools = pcall(require, "luarocks.fs."..loaded_platform..".tools")
|
||||
if ok and fs_plat_tools then
|
||||
load_fns(fs_plat_tools)
|
||||
load_fns(require("luarocks.fs.tools"))
|
||||
end
|
||||
|
||||
|
||||
return fs
|
||||
873
Utils/luarocks/lua/luarocks/fs/lua.lua
Normal file
873
Utils/luarocks/lua/luarocks/fs/lua.lua
Normal file
@ -0,0 +1,873 @@
|
||||
|
||||
--- Native Lua implementation of filesystem and platform abstractions,
|
||||
-- using LuaFileSystem, LZLib, MD5 and LuaCurl.
|
||||
-- module("luarocks.fs.lua")
|
||||
local fs_lua = {}
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
|
||||
local cfg = require("luarocks.cfg")
|
||||
local dir = require("luarocks.dir")
|
||||
local util = require("luarocks.util")
|
||||
local path = require("luarocks.path")
|
||||
|
||||
local socket_ok, zip_ok, unzip_ok, lfs_ok, md5_ok, posix_ok, _
|
||||
local http, ftp, lrzip, luazip, lfs, md5, posix
|
||||
|
||||
if cfg.fs_use_modules then
|
||||
socket_ok, http = pcall(require, "socket.http")
|
||||
_, ftp = pcall(require, "socket.ftp")
|
||||
zip_ok, lrzip = pcall(require, "luarocks.tools.zip")
|
||||
unzip_ok, luazip = pcall(require, "zip"); _G.zip = nil
|
||||
lfs_ok, lfs = pcall(require, "lfs")
|
||||
md5_ok, md5 = pcall(require, "md5")
|
||||
posix_ok, posix = pcall(require, "posix")
|
||||
end
|
||||
|
||||
local patch = require("luarocks.tools.patch")
|
||||
|
||||
local dir_stack = {}
|
||||
|
||||
local dir_separator = "/"
|
||||
|
||||
--- Test is file/dir is writable.
|
||||
-- Warning: testing if a file/dir is writable does not guarantee
|
||||
-- that it will remain writable and therefore it is no replacement
|
||||
-- for checking the result of subsequent operations.
|
||||
-- @param file string: filename to test
|
||||
-- @return boolean: true if file exists, false otherwise.
|
||||
function fs_lua.is_writable(file)
|
||||
assert(file)
|
||||
file = dir.normalize(file)
|
||||
local result
|
||||
if fs.is_dir(file) then
|
||||
local file2 = dir.path(file, '.tmpluarockstestwritable')
|
||||
local fh = io.open(file2, 'wb')
|
||||
result = fh ~= nil
|
||||
if fh then fh:close() end
|
||||
os.remove(file2)
|
||||
else
|
||||
local fh = io.open(file, 'r+b')
|
||||
result = fh ~= nil
|
||||
if fh then fh:close() end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function quote_args(command, ...)
|
||||
local out = { command }
|
||||
for _, arg in ipairs({...}) do
|
||||
assert(type(arg) == "string")
|
||||
out[#out+1] = fs.Q(arg)
|
||||
end
|
||||
return table.concat(out, " ")
|
||||
end
|
||||
|
||||
--- Run the given command, quoting its arguments.
|
||||
-- The command is executed in the current directory in the dir stack.
|
||||
-- @param command string: The command to be executed. No quoting/escaping
|
||||
-- is applied.
|
||||
-- @param ... Strings containing additional arguments, which are quoted.
|
||||
-- @return boolean: true if command succeeds (status code 0), false
|
||||
-- otherwise.
|
||||
function fs_lua.execute(command, ...)
|
||||
assert(type(command) == "string")
|
||||
return fs.execute_string(quote_args(command, ...))
|
||||
end
|
||||
|
||||
--- Run the given command, quoting its arguments, silencing its output.
|
||||
-- The command is executed in the current directory in the dir stack.
|
||||
-- Silencing is omitted if 'verbose' mode is enabled.
|
||||
-- @param command string: The command to be executed. No quoting/escaping
|
||||
-- is applied.
|
||||
-- @param ... Strings containing additional arguments, which will be quoted.
|
||||
-- @return boolean: true if command succeeds (status code 0), false
|
||||
-- otherwise.
|
||||
function fs_lua.execute_quiet(command, ...)
|
||||
assert(type(command) == "string")
|
||||
if cfg.verbose then -- omit silencing output
|
||||
return fs.execute_string(quote_args(command, ...))
|
||||
else
|
||||
return fs.execute_string(fs.quiet(quote_args(command, ...)))
|
||||
end
|
||||
end
|
||||
|
||||
--- Checks if the given tool is available.
|
||||
-- The tool is executed using a flag, usually just to ask its version.
|
||||
-- @param tool_cmd string: The command to be used to check the tool's presence (e.g. hg in case of Mercurial)
|
||||
-- @param tool_name string: The actual name of the tool (e.g. Mercurial)
|
||||
-- @param arg string: The flag to pass to the tool. '--version' by default.
|
||||
function fs_lua.is_tool_available(tool_cmd, tool_name, arg)
|
||||
assert(type(tool_cmd) == "string")
|
||||
assert(type(tool_name) == "string")
|
||||
|
||||
arg = arg or "--version"
|
||||
assert(type(arg) == "string")
|
||||
|
||||
if not fs.execute_quiet(fs.Q(tool_cmd), arg) then
|
||||
local msg = "'%s' program not found. Make sure %s is installed and is available in your PATH " ..
|
||||
"(or you may want to edit the 'variables.%s' value in file '%s')"
|
||||
return nil, msg:format(tool_cmd, tool_name, tool_name:upper(), cfg.which_config().nearest)
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
--- Check the MD5 checksum for a file.
|
||||
-- @param file string: The file to be checked.
|
||||
-- @param md5sum string: The string with the expected MD5 checksum.
|
||||
-- @return boolean: true if the MD5 checksum for 'file' equals 'md5sum', false + msg if not
|
||||
-- or if it could not perform the check for any reason.
|
||||
function fs_lua.check_md5(file, md5sum)
|
||||
file = dir.normalize(file)
|
||||
local computed, msg = fs.get_md5(file)
|
||||
if not computed then
|
||||
return false, msg
|
||||
end
|
||||
if computed:match("^"..md5sum) then
|
||||
return true
|
||||
else
|
||||
return false, "Mismatch MD5 hash for file "..file
|
||||
end
|
||||
end
|
||||
|
||||
--- List the contents of a directory.
|
||||
-- @param at string or nil: directory to list (will be the current
|
||||
-- directory if none is given).
|
||||
-- @return table: an array of strings with the filenames representing
|
||||
-- the contents of a directory.
|
||||
function fs_lua.list_dir(at)
|
||||
local result = {}
|
||||
for file in fs.dir(at) do
|
||||
result[#result+1] = file
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
--- Iterate over the contents of a directory.
|
||||
-- @param at string or nil: directory to list (will be the current
|
||||
-- directory if none is given).
|
||||
-- @return function: an iterator function suitable for use with
|
||||
-- the for statement.
|
||||
function fs_lua.dir(at)
|
||||
if not at then
|
||||
at = fs.current_dir()
|
||||
end
|
||||
at = dir.normalize(at)
|
||||
if not fs.is_dir(at) then
|
||||
return function() end
|
||||
end
|
||||
return coroutine.wrap(function() fs.dir_iterator(at) end)
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------
|
||||
-- LuaFileSystem functions
|
||||
---------------------------------------------------------------------
|
||||
|
||||
if lfs_ok then
|
||||
|
||||
--- Run the given command.
|
||||
-- The command is executed in the current directory in the dir stack.
|
||||
-- @param cmd string: No quoting/escaping is applied to the command.
|
||||
-- @return boolean: true if command succeeds (status code 0), false
|
||||
-- otherwise.
|
||||
function fs_lua.execute_string(cmd)
|
||||
local code = os.execute(cmd)
|
||||
return (code == 0 or code == true)
|
||||
end
|
||||
|
||||
--- Obtain current directory.
|
||||
-- Uses the module's internal dir stack.
|
||||
-- @return string: the absolute pathname of the current directory.
|
||||
function fs_lua.current_dir()
|
||||
return lfs.currentdir()
|
||||
end
|
||||
|
||||
--- Change the current directory.
|
||||
-- Uses the module's internal dir stack. This does not have exact
|
||||
-- semantics of chdir, as it does not handle errors the same way,
|
||||
-- but works well for our purposes for now.
|
||||
-- @param d string: The directory to switch to.
|
||||
function fs_lua.change_dir(d)
|
||||
table.insert(dir_stack, lfs.currentdir())
|
||||
d = dir.normalize(d)
|
||||
return lfs.chdir(d)
|
||||
end
|
||||
|
||||
--- Change directory to root.
|
||||
-- Allows leaving a directory (e.g. for deleting it) in
|
||||
-- a crossplatform way.
|
||||
function fs_lua.change_dir_to_root()
|
||||
local current = lfs.currentdir()
|
||||
if not current or current == "" then
|
||||
return false
|
||||
end
|
||||
table.insert(dir_stack, current)
|
||||
lfs.chdir("/") -- works on Windows too
|
||||
return true
|
||||
end
|
||||
|
||||
--- Change working directory to the previous in the dir stack.
|
||||
-- @return true if a pop ocurred, false if the stack was empty.
|
||||
function fs_lua.pop_dir()
|
||||
local d = table.remove(dir_stack)
|
||||
if d then
|
||||
lfs.chdir(d)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- Create a directory if it does not already exist.
|
||||
-- If any of the higher levels in the path name do not exist
|
||||
-- too, they are created as well.
|
||||
-- @param directory string: pathname of directory to create.
|
||||
-- @return boolean or (boolean, string): true on success or (false, error message) on failure.
|
||||
function fs_lua.make_dir(directory)
|
||||
assert(type(directory) == "string")
|
||||
directory = dir.normalize(directory)
|
||||
local path = nil
|
||||
if directory:sub(2, 2) == ":" then
|
||||
path = directory:sub(1, 2)
|
||||
directory = directory:sub(4)
|
||||
else
|
||||
if directory:match("^/") then
|
||||
path = ""
|
||||
end
|
||||
end
|
||||
for d in directory:gmatch("([^"..dir.separator.."]+)"..dir.separator.."*") do
|
||||
path = path and path .. dir.separator .. d or d
|
||||
local mode = lfs.attributes(path, "mode")
|
||||
if not mode then
|
||||
local ok, err = lfs.mkdir(path)
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
ok, err = fs.chmod(path, cfg.perm_exec)
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
elseif mode ~= "directory" then
|
||||
return false, path.." is not a directory"
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Remove a directory if it is empty.
|
||||
-- Does not return errors (for example, if directory is not empty or
|
||||
-- if already does not exist)
|
||||
-- @param d string: pathname of directory to remove.
|
||||
function fs_lua.remove_dir_if_empty(d)
|
||||
assert(d)
|
||||
d = dir.normalize(d)
|
||||
lfs.rmdir(d)
|
||||
end
|
||||
|
||||
--- Remove a directory if it is empty.
|
||||
-- Does not return errors (for example, if directory is not empty or
|
||||
-- if already does not exist)
|
||||
-- @param d string: pathname of directory to remove.
|
||||
function fs_lua.remove_dir_tree_if_empty(d)
|
||||
assert(d)
|
||||
d = dir.normalize(d)
|
||||
for i=1,10 do
|
||||
lfs.rmdir(d)
|
||||
d = dir.dir_name(d)
|
||||
end
|
||||
end
|
||||
|
||||
--- Copy a file.
|
||||
-- @param src string: Pathname of source
|
||||
-- @param dest string: Pathname of destination
|
||||
-- @param perms string or nil: Permissions for destination file,
|
||||
-- or nil to use the source filename permissions
|
||||
-- @return boolean or (boolean, string): true on success, false on failure,
|
||||
-- plus an error message.
|
||||
function fs_lua.copy(src, dest, perms)
|
||||
assert(src and dest)
|
||||
src = dir.normalize(src)
|
||||
dest = dir.normalize(dest)
|
||||
local destmode = lfs.attributes(dest, "mode")
|
||||
if destmode == "directory" then
|
||||
dest = dir.path(dest, dir.base_name(src))
|
||||
end
|
||||
if not perms then perms = fs.get_permissions(src) end
|
||||
local src_h, err = io.open(src, "rb")
|
||||
if not src_h then return nil, err end
|
||||
local dest_h, err = io.open(dest, "w+b")
|
||||
if not dest_h then src_h:close() return nil, err end
|
||||
while true do
|
||||
local block = src_h:read(8192)
|
||||
if not block then break end
|
||||
dest_h:write(block)
|
||||
end
|
||||
src_h:close()
|
||||
dest_h:close()
|
||||
fs.chmod(dest, perms)
|
||||
return true
|
||||
end
|
||||
|
||||
--- Implementation function for recursive copy of directory contents.
|
||||
-- Assumes paths are normalized.
|
||||
-- @param src string: Pathname of source
|
||||
-- @param dest string: Pathname of destination
|
||||
-- @param perms string or nil: Optional permissions.
|
||||
-- If not given, permissions of the source are copied over to the destination.
|
||||
-- @return boolean or (boolean, string): true on success, false on failure
|
||||
local function recursive_copy(src, dest, perms)
|
||||
local srcmode = lfs.attributes(src, "mode")
|
||||
|
||||
if srcmode == "file" then
|
||||
local ok = fs.copy(src, dest, perms)
|
||||
if not ok then return false end
|
||||
elseif srcmode == "directory" then
|
||||
local subdir = dir.path(dest, dir.base_name(src))
|
||||
local ok, err = fs.make_dir(subdir)
|
||||
if not ok then return nil, err end
|
||||
for file in lfs.dir(src) do
|
||||
if file ~= "." and file ~= ".." then
|
||||
local ok = recursive_copy(dir.path(src, file), subdir, perms)
|
||||
if not ok then return false end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Recursively copy the contents of a directory.
|
||||
-- @param src string: Pathname of source
|
||||
-- @param dest string: Pathname of destination
|
||||
-- @param perms string or nil: Optional permissions.
|
||||
-- @return boolean or (boolean, string): true on success, false on failure,
|
||||
-- plus an error message.
|
||||
function fs_lua.copy_contents(src, dest, perms)
|
||||
assert(src and dest)
|
||||
src = dir.normalize(src)
|
||||
dest = dir.normalize(dest)
|
||||
assert(lfs.attributes(src, "mode") == "directory")
|
||||
|
||||
for file in lfs.dir(src) do
|
||||
if file ~= "." and file ~= ".." then
|
||||
local ok = recursive_copy(dir.path(src, file), dest, perms)
|
||||
if not ok then
|
||||
return false, "Failed copying "..src.." to "..dest
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Implementation function for recursive removal of directories.
|
||||
-- Assumes paths are normalized.
|
||||
-- @param name string: Pathname of file
|
||||
-- @return boolean or (boolean, string): true on success,
|
||||
-- or nil and an error message on failure.
|
||||
local function recursive_delete(name)
|
||||
local ok = os.remove(name)
|
||||
if ok then return true end
|
||||
local pok, ok, err = pcall(function()
|
||||
for file in lfs.dir(name) do
|
||||
if file ~= "." and file ~= ".." then
|
||||
local ok, err = recursive_delete(dir.path(name, file))
|
||||
if not ok then return nil, err end
|
||||
end
|
||||
end
|
||||
local ok, err = lfs.rmdir(name)
|
||||
return ok, (not ok) and err
|
||||
end)
|
||||
if pok then
|
||||
return ok, err
|
||||
else
|
||||
return pok, ok
|
||||
end
|
||||
end
|
||||
|
||||
--- Delete a file or a directory and all its contents.
|
||||
-- @param name string: Pathname of source
|
||||
-- @return nil
|
||||
function fs_lua.delete(name)
|
||||
name = dir.normalize(name)
|
||||
recursive_delete(name)
|
||||
end
|
||||
|
||||
--- Internal implementation function for fs.dir.
|
||||
-- Yields a filename on each iteration.
|
||||
-- @param at string: directory to list
|
||||
-- @return nil
|
||||
function fs_lua.dir_iterator(at)
|
||||
for file in lfs.dir(at) do
|
||||
if file ~= "." and file ~= ".." then
|
||||
coroutine.yield(file)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Implementation function for recursive find.
|
||||
-- Assumes paths are normalized.
|
||||
-- @param cwd string: Current working directory in recursion.
|
||||
-- @param prefix string: Auxiliary prefix string to form pathname.
|
||||
-- @param result table: Array of strings where results are collected.
|
||||
local function recursive_find(cwd, prefix, result)
|
||||
for file in lfs.dir(cwd) do
|
||||
if file ~= "." and file ~= ".." then
|
||||
local item = prefix .. file
|
||||
table.insert(result, item)
|
||||
local pathname = dir.path(cwd, file)
|
||||
if lfs.attributes(pathname, "mode") == "directory" then
|
||||
recursive_find(pathname, item..dir_separator, result)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Recursively scan the contents of a directory.
|
||||
-- @param at string or nil: directory to scan (will be the current
|
||||
-- directory if none is given).
|
||||
-- @return table: an array of strings with the filenames representing
|
||||
-- the contents of a directory.
|
||||
function fs_lua.find(at)
|
||||
assert(type(at) == "string" or not at)
|
||||
if not at then
|
||||
at = fs.current_dir()
|
||||
end
|
||||
at = dir.normalize(at)
|
||||
if not fs.is_dir(at) then
|
||||
return {}
|
||||
end
|
||||
local result = {}
|
||||
recursive_find(at, "", result)
|
||||
return result
|
||||
end
|
||||
|
||||
--- Test for existance of a file.
|
||||
-- @param file string: filename to test
|
||||
-- @return boolean: true if file exists, false otherwise.
|
||||
function fs_lua.exists(file)
|
||||
assert(file)
|
||||
file = dir.normalize(file)
|
||||
return type(lfs.attributes(file)) == "table"
|
||||
end
|
||||
|
||||
--- Test is pathname is a directory.
|
||||
-- @param file string: pathname to test
|
||||
-- @return boolean: true if it is a directory, false otherwise.
|
||||
function fs_lua.is_dir(file)
|
||||
assert(file)
|
||||
file = dir.normalize(file)
|
||||
return lfs.attributes(file, "mode") == "directory"
|
||||
end
|
||||
|
||||
--- Test is pathname is a regular file.
|
||||
-- @param file string: pathname to test
|
||||
-- @return boolean: true if it is a file, false otherwise.
|
||||
function fs_lua.is_file(file)
|
||||
assert(file)
|
||||
file = dir.normalize(file)
|
||||
return lfs.attributes(file, "mode") == "file"
|
||||
end
|
||||
|
||||
function fs_lua.set_time(file, time)
|
||||
file = dir.normalize(file)
|
||||
return lfs.touch(file, time)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------
|
||||
-- LuaZip functions
|
||||
---------------------------------------------------------------------
|
||||
|
||||
if zip_ok then
|
||||
|
||||
function fs_lua.zip(zipfile, ...)
|
||||
return lrzip.zip(zipfile, ...)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if unzip_ok then
|
||||
--- Uncompress files from a .zip archive.
|
||||
-- @param zipfile string: pathname of .zip archive to be extracted.
|
||||
-- @return boolean: true on success, false on failure.
|
||||
function fs_lua.unzip(zipfile)
|
||||
local zipfile, err = luazip.open(zipfile)
|
||||
if not zipfile then return nil, err end
|
||||
local files = zipfile:files()
|
||||
local file = files()
|
||||
repeat
|
||||
if file.filename:sub(#file.filename) == "/" then
|
||||
local ok, err = fs.make_dir(dir.path(fs.current_dir(), file.filename))
|
||||
if not ok then return nil, err end
|
||||
else
|
||||
local base = dir.dir_name(file.filename)
|
||||
if base ~= "" then
|
||||
base = dir.path(fs.current_dir(), base)
|
||||
if not fs.is_dir(base) then
|
||||
local ok, err = fs.make_dir(base)
|
||||
if not ok then return nil, err end
|
||||
end
|
||||
end
|
||||
local rf, err = zipfile:open(file.filename)
|
||||
if not rf then zipfile:close(); return nil, err end
|
||||
local contents = rf:read("*a")
|
||||
rf:close()
|
||||
local wf, err = io.open(dir.path(fs.current_dir(), file.filename), "wb")
|
||||
if not wf then zipfile:close(); return nil, err end
|
||||
wf:write(contents)
|
||||
wf:close()
|
||||
end
|
||||
file = files()
|
||||
until not file
|
||||
zipfile:close()
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------
|
||||
-- LuaSocket functions
|
||||
---------------------------------------------------------------------
|
||||
|
||||
if socket_ok then
|
||||
|
||||
local ltn12 = require("ltn12")
|
||||
local luasec_ok, https = pcall(require, "ssl.https")
|
||||
|
||||
local redirect_protocols = {
|
||||
http = http,
|
||||
https = luasec_ok and https,
|
||||
}
|
||||
|
||||
local function request(url, method, http, loop_control)
|
||||
local result = {}
|
||||
|
||||
local proxy = cfg.http_proxy
|
||||
if type(proxy) ~= "string" then proxy = nil end
|
||||
-- LuaSocket's http.request crashes when given URLs missing the scheme part.
|
||||
if proxy and not proxy:find("://") then
|
||||
proxy = "http://" .. proxy
|
||||
end
|
||||
|
||||
if cfg.show_downloads then
|
||||
io.write(method.." "..url.." ...\n")
|
||||
end
|
||||
local dots = 0
|
||||
if cfg.connection_timeout and cfg.connection_timeout > 0 then
|
||||
http.TIMEOUT = cfg.connection_timeout
|
||||
end
|
||||
local res, status, headers, err = http.request {
|
||||
url = url,
|
||||
proxy = proxy,
|
||||
method = method,
|
||||
redirect = false,
|
||||
sink = ltn12.sink.table(result),
|
||||
step = cfg.show_downloads and function(...)
|
||||
io.write(".")
|
||||
io.flush()
|
||||
dots = dots + 1
|
||||
if dots == 70 then
|
||||
io.write("\n")
|
||||
dots = 0
|
||||
end
|
||||
return ltn12.pump.step(...)
|
||||
end,
|
||||
headers = {
|
||||
["user-agent"] = cfg.user_agent.." via LuaSocket"
|
||||
},
|
||||
}
|
||||
if cfg.show_downloads then
|
||||
io.write("\n")
|
||||
end
|
||||
if not res then
|
||||
return nil, status
|
||||
elseif status == 301 or status == 302 then
|
||||
local location = headers.location
|
||||
if location then
|
||||
local protocol, rest = dir.split_url(location)
|
||||
if redirect_protocols[protocol] then
|
||||
if not loop_control then
|
||||
loop_control = {}
|
||||
elseif loop_control[location] then
|
||||
return nil, "Redirection loop -- broken URL?"
|
||||
end
|
||||
loop_control[url] = true
|
||||
return request(location, method, redirect_protocols[protocol], loop_control)
|
||||
else
|
||||
return nil, "URL redirected to unsupported protocol - install luasec to get HTTPS support.", "https"
|
||||
end
|
||||
end
|
||||
return nil, err
|
||||
elseif status ~= 200 then
|
||||
return nil, err
|
||||
else
|
||||
return result, status, headers, err
|
||||
end
|
||||
end
|
||||
|
||||
local function http_request(url, http, cached)
|
||||
if cached then
|
||||
local tsfd = io.open(cached..".timestamp", "r")
|
||||
if tsfd then
|
||||
local timestamp = tsfd:read("*a")
|
||||
tsfd:close()
|
||||
local result, status, headers, err = request(url, "HEAD", http)
|
||||
if status == 200 and headers["last-modified"] == timestamp then
|
||||
return true
|
||||
end
|
||||
if not result then
|
||||
return nil, status, headers
|
||||
end
|
||||
end
|
||||
end
|
||||
local result, status, headers, err = request(url, "GET", http)
|
||||
if result then
|
||||
if cached and headers["last-modified"] then
|
||||
local tsfd = io.open(cached..".timestamp", "w")
|
||||
if tsfd then
|
||||
tsfd:write(headers["last-modified"])
|
||||
tsfd:close()
|
||||
end
|
||||
end
|
||||
return table.concat(result)
|
||||
else
|
||||
return nil, status, headers
|
||||
end
|
||||
end
|
||||
|
||||
local downloader_warning = false
|
||||
|
||||
--- Download a remote file.
|
||||
-- @param url string: URL to be fetched.
|
||||
-- @param filename string or nil: this function attempts to detect the
|
||||
-- resulting local filename of the remote file as the basename of the URL;
|
||||
-- if that is not correct (due to a redirection, for example), the local
|
||||
-- filename can be given explicitly as this second argument.
|
||||
-- @return (boolean, string): true and the filename on success,
|
||||
-- false and the error message on failure.
|
||||
function fs_lua.download(url, filename, cache)
|
||||
assert(type(url) == "string")
|
||||
assert(type(filename) == "string" or not filename)
|
||||
|
||||
filename = fs.absolute_name(filename or dir.base_name(url))
|
||||
|
||||
-- delegate to the configured downloader so we don't have to deal with whitelists
|
||||
if cfg.no_proxy then
|
||||
return fs.use_downloader(url, filename, cache)
|
||||
end
|
||||
|
||||
local content, err, https_err
|
||||
if util.starts_with(url, "http:") then
|
||||
content, err, https_err = http_request(url, http, cache and filename)
|
||||
elseif util.starts_with(url, "ftp:") then
|
||||
content, err = ftp.get(url)
|
||||
elseif util.starts_with(url, "https:") then
|
||||
-- skip LuaSec when proxy is enabled since it is not supported
|
||||
if luasec_ok and not cfg.https_proxy then
|
||||
content, err = http_request(url, https, cache and filename)
|
||||
else
|
||||
https_err = true
|
||||
end
|
||||
else
|
||||
err = "Unsupported protocol"
|
||||
end
|
||||
if https_err then
|
||||
if not downloader_warning then
|
||||
util.printerr("Warning: falling back to "..cfg.downloader.." - install luasec to get native HTTPS support")
|
||||
downloader_warning = true
|
||||
end
|
||||
return fs.use_downloader(url, filename, cache)
|
||||
end
|
||||
if cache and content == true then
|
||||
return true, filename
|
||||
end
|
||||
if not content then
|
||||
return false, tostring(err)
|
||||
end
|
||||
local file = io.open(filename, "wb")
|
||||
if not file then return false end
|
||||
file:write(content)
|
||||
file:close()
|
||||
return true, filename
|
||||
end
|
||||
|
||||
else --...if socket_ok == false then
|
||||
|
||||
function fs_lua.download(url, filename, cache)
|
||||
return fs.use_downloader(url, filename, cache)
|
||||
end
|
||||
|
||||
end
|
||||
---------------------------------------------------------------------
|
||||
-- MD5 functions
|
||||
---------------------------------------------------------------------
|
||||
|
||||
if md5_ok then
|
||||
|
||||
-- Support the interface of lmd5 by lhf in addition to md5 by Roberto
|
||||
-- and the keplerproject.
|
||||
if not md5.sumhexa and md5.digest then
|
||||
md5.sumhexa = function(msg)
|
||||
return md5.digest(msg)
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the MD5 checksum for a file.
|
||||
-- @param file string: The file to be computed.
|
||||
-- @return string: The MD5 checksum or nil + error
|
||||
function fs_lua.get_md5(file)
|
||||
file = fs.absolute_name(file)
|
||||
local file_handler = io.open(file, "rb")
|
||||
if not file_handler then return nil, "Failed to open file for reading: "..file end
|
||||
local computed = md5.sumhexa(file_handler:read("*a"))
|
||||
file_handler:close()
|
||||
if computed then return computed end
|
||||
return nil, "Failed to compute MD5 hash for file "..file
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------
|
||||
-- POSIX functions
|
||||
---------------------------------------------------------------------
|
||||
|
||||
if posix_ok then
|
||||
|
||||
local octal_to_rwx = {
|
||||
["0"] = "---",
|
||||
["1"] = "--x",
|
||||
["2"] = "-w-",
|
||||
["3"] = "-wx",
|
||||
["4"] = "r--",
|
||||
["5"] = "r-x",
|
||||
["6"] = "rw-",
|
||||
["7"] = "rwx",
|
||||
}
|
||||
|
||||
function fs_lua.chmod(file, mode)
|
||||
-- LuaPosix (as of 5.1.15) does not support octal notation...
|
||||
if mode:sub(1,1) == "0" then
|
||||
local new_mode = {}
|
||||
for c in mode:sub(-3):gmatch(".") do
|
||||
table.insert(new_mode, octal_to_rwx[c])
|
||||
end
|
||||
mode = table.concat(new_mode)
|
||||
end
|
||||
local err = posix.chmod(file, mode)
|
||||
return err == 0
|
||||
end
|
||||
|
||||
function fs_lua.get_permissions(file)
|
||||
return posix.stat(file, "mode")
|
||||
end
|
||||
|
||||
--- Create a temporary directory.
|
||||
-- @param name string: name pattern to use for avoiding conflicts
|
||||
-- when creating temporary directory.
|
||||
-- @return string or (nil, string): name of temporary directory or (nil, error message) on failure.
|
||||
function fs_lua.make_temp_dir(name)
|
||||
assert(type(name) == "string")
|
||||
name = dir.normalize(name)
|
||||
|
||||
return posix.mkdtemp((os.getenv("TMPDIR") or "/tmp") .. "/luarocks_" .. name:gsub(dir.separator, "_") .. "-XXXXXX")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------
|
||||
-- Other functions
|
||||
---------------------------------------------------------------------
|
||||
|
||||
--- Apply a patch.
|
||||
-- @param patchname string: The filename of the patch.
|
||||
-- @param patchdata string or nil: The actual patch as a string.
|
||||
function fs_lua.apply_patch(patchname, patchdata)
|
||||
local p, all_ok = patch.read_patch(patchname, patchdata)
|
||||
if not all_ok then
|
||||
return nil, "Failed reading patch "..patchname
|
||||
end
|
||||
if p then
|
||||
return patch.apply_patch(p, 1)
|
||||
end
|
||||
end
|
||||
|
||||
--- Move a file.
|
||||
-- @param src string: Pathname of source
|
||||
-- @param dest string: Pathname of destination
|
||||
-- @param perms string or nil: Permissions for destination file,
|
||||
-- or nil to use the source filename permissions.
|
||||
-- @return boolean or (boolean, string): true on success, false on failure,
|
||||
-- plus an error message.
|
||||
function fs_lua.move(src, dest, perms)
|
||||
assert(src and dest)
|
||||
if fs.exists(dest) and not fs.is_dir(dest) then
|
||||
return false, "File already exists: "..dest
|
||||
end
|
||||
local ok, err = fs.copy(src, dest, perms)
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
fs.delete(src)
|
||||
if fs.exists(src) then
|
||||
return false, "Failed move: could not delete "..src.." after copy."
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Check if user has write permissions for the command.
|
||||
-- Assumes the configuration variables under cfg have been previously set up.
|
||||
-- @param flags table: the flags table passed to run() drivers.
|
||||
-- @return boolean or (boolean, string): true on success, false on failure,
|
||||
-- plus an error message.
|
||||
function fs_lua.check_command_permissions(flags)
|
||||
local root_dir = path.root_dir(cfg.rocks_dir)
|
||||
local ok = true
|
||||
local err = ""
|
||||
for _, dir in ipairs { cfg.rocks_dir, root_dir } do
|
||||
if fs.exists(dir) and not fs.is_writable(dir) then
|
||||
ok = false
|
||||
err = "Your user does not have write permissions in " .. dir
|
||||
break
|
||||
end
|
||||
end
|
||||
if ok and not fs.exists(root_dir) then
|
||||
local root = fs.root_of(root_dir)
|
||||
local parent = root_dir
|
||||
repeat
|
||||
parent = dir.dir_name(parent)
|
||||
if parent == "" then
|
||||
parent = root
|
||||
end
|
||||
until parent == root or fs.exists(parent)
|
||||
if not fs.is_writable(parent) then
|
||||
ok = false
|
||||
err = root_dir.." does not exist and your user does not have write permissions in " .. parent
|
||||
end
|
||||
end
|
||||
if ok then
|
||||
return true
|
||||
else
|
||||
if flags["local"] then
|
||||
err = err .. " \n-- please check your permissions."
|
||||
else
|
||||
err = err .. " \n-- you may want to run as a privileged user or use your local tree with --local."
|
||||
end
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
|
||||
--- Check whether a file is a Lua script
|
||||
-- When the file can be succesfully compiled by the configured
|
||||
-- Lua interpreter, it's considered to be a valid Lua file.
|
||||
-- @param name filename of file to check
|
||||
-- @return boolean true, if it is a Lua script, false otherwise
|
||||
function fs_lua.is_lua(name)
|
||||
name = name:gsub([[%\]],"/") -- normalize on fw slash to prevent escaping issues
|
||||
local lua = fs.Q(dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)) -- get lua interpreter configured
|
||||
-- execute on configured interpreter, might not be the same as the interpreter LR is run on
|
||||
local result = fs.execute_string(lua..[[ -e "if loadfile(']]..name..[[') then os.exit() else os.exit(1) end"]])
|
||||
return (result == true)
|
||||
end
|
||||
|
||||
return fs_lua
|
||||
156
Utils/luarocks/lua/luarocks/fs/tools.lua
Normal file
156
Utils/luarocks/lua/luarocks/fs/tools.lua
Normal file
@ -0,0 +1,156 @@
|
||||
|
||||
--- Common fs operations implemented with third-party tools.
|
||||
local tools = {}
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
local dir = require("luarocks.dir")
|
||||
local cfg = require("luarocks.cfg")
|
||||
|
||||
local vars = cfg.variables
|
||||
|
||||
local dir_stack = {}
|
||||
|
||||
--- Obtain current directory.
|
||||
-- Uses the module's internal directory stack.
|
||||
-- @return string: the absolute pathname of the current directory.
|
||||
function tools.current_dir()
|
||||
local current = cfg.cache_pwd
|
||||
if not current then
|
||||
local pipe = io.popen(fs.quiet_stderr(fs.Q(vars.PWD)))
|
||||
current = pipe:read("*l")
|
||||
pipe:close()
|
||||
cfg.cache_pwd = current
|
||||
end
|
||||
for _, directory in ipairs(dir_stack) do
|
||||
current = fs.absolute_name(directory, current)
|
||||
end
|
||||
return current
|
||||
end
|
||||
|
||||
--- Change the current directory.
|
||||
-- Uses the module's internal directory stack. This does not have exact
|
||||
-- semantics of chdir, as it does not handle errors the same way,
|
||||
-- but works well for our purposes for now.
|
||||
-- @param directory string: The directory to switch to.
|
||||
-- @return boolean or (nil, string): true if successful, (nil, error message) if failed.
|
||||
function tools.change_dir(directory)
|
||||
assert(type(directory) == "string")
|
||||
if fs.is_dir(directory) then
|
||||
table.insert(dir_stack, directory)
|
||||
return true
|
||||
end
|
||||
return nil, "directory not found: "..directory
|
||||
end
|
||||
|
||||
--- Change directory to root.
|
||||
-- Allows leaving a directory (e.g. for deleting it) in
|
||||
-- a crossplatform way.
|
||||
function tools.change_dir_to_root()
|
||||
table.insert(dir_stack, "/")
|
||||
end
|
||||
|
||||
--- Change working directory to the previous in the directory stack.
|
||||
function tools.pop_dir()
|
||||
local directory = table.remove(dir_stack)
|
||||
return directory ~= nil
|
||||
end
|
||||
|
||||
--- Run the given command.
|
||||
-- The command is executed in the current directory in the directory stack.
|
||||
-- @param cmd string: No quoting/escaping is applied to the command.
|
||||
-- @return boolean: true if command succeeds (status code 0), false
|
||||
-- otherwise.
|
||||
function tools.execute_string(cmd)
|
||||
local current = fs.current_dir()
|
||||
if not current then return false end
|
||||
cmd = fs.command_at(current, cmd)
|
||||
local code = os.execute(cmd)
|
||||
if code == 0 or code == true then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- Internal implementation function for fs.dir.
|
||||
-- Yields a filename on each iteration.
|
||||
-- @param at string: directory to list
|
||||
-- @return nil
|
||||
function tools.dir_iterator(at)
|
||||
local pipe = io.popen(fs.command_at(at, fs.Q(vars.LS)))
|
||||
for file in pipe:lines() do
|
||||
if file ~= "." and file ~= ".." then
|
||||
coroutine.yield(file)
|
||||
end
|
||||
end
|
||||
pipe:close()
|
||||
end
|
||||
|
||||
--- Download a remote file.
|
||||
-- @param url string: URL to be fetched.
|
||||
-- @param filename string or nil: this function attempts to detect the
|
||||
-- resulting local filename of the remote file as the basename of the URL;
|
||||
-- if that is not correct (due to a redirection, for example), the local
|
||||
-- filename can be given explicitly as this second argument.
|
||||
-- @return (boolean, string): true and the filename on success,
|
||||
-- false and the error message on failure.
|
||||
function tools.use_downloader(url, filename, cache)
|
||||
assert(type(url) == "string")
|
||||
assert(type(filename) == "string" or not filename)
|
||||
|
||||
filename = fs.absolute_name(filename or dir.base_name(url))
|
||||
|
||||
local ok
|
||||
if cfg.downloader == "wget" then
|
||||
local wget_cmd = fs.Q(vars.WGET).." "..vars.WGETNOCERTFLAG.." --no-cache --user-agent=\""..cfg.user_agent.." via wget\" --quiet "
|
||||
if cfg.connection_timeout and cfg.connection_timeout > 0 then
|
||||
wget_cmd = wget_cmd .. "--timeout="..tonumber(cfg.connection_timeout).." --tries=1 "
|
||||
end
|
||||
if cache then
|
||||
-- --timestamping is incompatible with --output-document,
|
||||
-- but that's not a problem for our use cases.
|
||||
fs.change_dir(dir.dir_name(filename))
|
||||
ok = fs.execute_quiet(wget_cmd.." --timestamping ", url)
|
||||
fs.pop_dir()
|
||||
elseif filename then
|
||||
ok = fs.execute_quiet(wget_cmd.." --output-document ", filename, url)
|
||||
else
|
||||
ok = fs.execute_quiet(wget_cmd, url)
|
||||
end
|
||||
elseif cfg.downloader == "curl" then
|
||||
local curl_cmd = fs.Q(vars.CURL).." "..vars.CURLNOCERTFLAG.." -f -L --user-agent \""..cfg.user_agent.." via curl\" "
|
||||
if cfg.connection_timeout and cfg.connection_timeout > 0 then
|
||||
curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." "
|
||||
end
|
||||
ok = fs.execute_string(fs.quiet_stderr(curl_cmd..fs.Q(url).." > "..fs.Q(filename)))
|
||||
end
|
||||
if ok then
|
||||
return true, filename
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local md5_cmd = {
|
||||
md5sum = fs.Q(vars.MD5SUM),
|
||||
openssl = fs.Q(vars.OPENSSL).." md5",
|
||||
md5 = fs.Q(vars.MD5),
|
||||
}
|
||||
|
||||
--- Get the MD5 checksum for a file.
|
||||
-- @param file string: The file to be computed.
|
||||
-- @return string: The MD5 checksum or nil + message
|
||||
function tools.get_md5(file)
|
||||
local cmd = md5_cmd[cfg.md5checker]
|
||||
if not cmd then return nil, "no MD5 checker command configured" end
|
||||
local pipe = io.popen(cmd.." "..fs.Q(fs.absolute_name(file)))
|
||||
local computed = pipe:read("*a")
|
||||
pipe:close()
|
||||
if computed then
|
||||
computed = computed:match("("..("%x"):rep(32)..")")
|
||||
end
|
||||
if computed then return computed end
|
||||
return nil, "Failed to compute MD5 hash for file "..tostring(fs.absolute_name(file))
|
||||
end
|
||||
|
||||
return tools
|
||||
135
Utils/luarocks/lua/luarocks/fs/unix.lua
Normal file
135
Utils/luarocks/lua/luarocks/fs/unix.lua
Normal file
@ -0,0 +1,135 @@
|
||||
|
||||
--- Unix implementation of filesystem and platform abstractions.
|
||||
local unix = {}
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
|
||||
local cfg = require("luarocks.cfg")
|
||||
local dir = require("luarocks.dir")
|
||||
local util = require("luarocks.util")
|
||||
|
||||
--- Annotate command string for quiet execution.
|
||||
-- @param cmd string: A command-line string.
|
||||
-- @return string: The command-line, with silencing annotation.
|
||||
function unix.quiet(cmd)
|
||||
return cmd.." 1> /dev/null 2> /dev/null"
|
||||
end
|
||||
|
||||
--- Annotate command string for execution with quiet stderr.
|
||||
-- @param cmd string: A command-line string.
|
||||
-- @return string: The command-line, with stderr silencing annotation.
|
||||
function unix.quiet_stderr(cmd)
|
||||
return cmd.." 2> /dev/null"
|
||||
end
|
||||
|
||||
--- Quote argument for shell processing.
|
||||
-- Adds single quotes and escapes.
|
||||
-- @param arg string: Unquoted argument.
|
||||
-- @return string: Quoted argument.
|
||||
function unix.Q(arg)
|
||||
assert(type(arg) == "string")
|
||||
return "'" .. arg:gsub("'", "'\\''") .. "'"
|
||||
end
|
||||
|
||||
--- Return an absolute pathname from a potentially relative one.
|
||||
-- @param pathname string: pathname to convert.
|
||||
-- @param relative_to string or nil: path to prepend when making
|
||||
-- pathname absolute, or the current dir in the dir stack if
|
||||
-- not given.
|
||||
-- @return string: The pathname converted to absolute.
|
||||
function unix.absolute_name(pathname, relative_to)
|
||||
assert(type(pathname) == "string")
|
||||
assert(type(relative_to) == "string" or not relative_to)
|
||||
|
||||
relative_to = relative_to or fs.current_dir()
|
||||
if pathname:sub(1,1) == "/" then
|
||||
return pathname
|
||||
else
|
||||
return relative_to .. "/" .. pathname
|
||||
end
|
||||
end
|
||||
|
||||
--- Return the root directory for the given path.
|
||||
-- In Unix, root is always "/".
|
||||
-- @param pathname string: pathname to use.
|
||||
-- @return string: The root of the given pathname.
|
||||
function unix.root_of(_)
|
||||
return "/"
|
||||
end
|
||||
|
||||
--- Create a wrapper to make a script executable from the command-line.
|
||||
-- @param file string: Pathname of script to be made executable.
|
||||
-- @param dest string: Directory where to put the wrapper.
|
||||
-- @param name string: rock name to be used in loader context.
|
||||
-- @param version string: rock version to be used in loader context.
|
||||
-- @return boolean or (nil, string): True if succeeded, or nil and
|
||||
-- an error message.
|
||||
function unix.wrap_script(file, dest, name, version)
|
||||
assert(type(file) == "string")
|
||||
assert(type(dest) == "string")
|
||||
|
||||
local base = dir.base_name(file)
|
||||
local wrapname = fs.is_dir(dest) and dest.."/"..base or dest
|
||||
local lpath, lcpath = cfg.package_paths()
|
||||
local wrapper = io.open(wrapname, "w")
|
||||
if not wrapper then
|
||||
return nil, "Could not open "..wrapname.." for writing."
|
||||
end
|
||||
wrapper:write("#!/bin/sh\n\n")
|
||||
local lua = dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)
|
||||
local ppaths = "package.path="..util.LQ(lpath..";").."..package.path; package.cpath="..util.LQ(lcpath..";").."..package.cpath"
|
||||
local addctx = "local k,l,_=pcall(require,"..util.LQ("luarocks.loader")..") _=k and l.add_context("..util.LQ(name)..","..util.LQ(version)..")"
|
||||
wrapper:write('exec '..fs.Q(lua)..' -e '..fs.Q(ppaths)..' -e '..fs.Q(addctx)..' '..fs.Q(file)..' "$@"\n')
|
||||
wrapper:close()
|
||||
if fs.chmod(wrapname, cfg.perm_exec) then
|
||||
return true
|
||||
else
|
||||
return nil, "Could not make "..wrapname.." executable."
|
||||
end
|
||||
end
|
||||
|
||||
--- Check if a file (typically inside path.bin_dir) is an actual binary
|
||||
-- or a Lua wrapper.
|
||||
-- @param filename string: the file name with full path.
|
||||
-- @return boolean: returns true if file is an actual binary
|
||||
-- (or if it couldn't check) or false if it is a Lua wrapper.
|
||||
function unix.is_actual_binary(filename)
|
||||
if filename:match("%.lua$") then
|
||||
return false
|
||||
end
|
||||
local file = io.open(filename)
|
||||
if not file then
|
||||
return true
|
||||
end
|
||||
local first = file:read(2)
|
||||
file:close()
|
||||
if not first then
|
||||
util.printerr("Warning: could not read "..filename)
|
||||
return true
|
||||
end
|
||||
return first ~= "#!"
|
||||
end
|
||||
|
||||
function unix.copy_binary(filename, dest)
|
||||
return fs.copy(filename, dest, cfg.perm_exec)
|
||||
end
|
||||
|
||||
--- Move a file on top of the other.
|
||||
-- The new file ceases to exist under its original name,
|
||||
-- and takes over the name of the old file.
|
||||
-- On Unix this is done through a single rename operation.
|
||||
-- @param old_file The name of the original file,
|
||||
-- which will be the new name of new_file.
|
||||
-- @param new_file The name of the new file,
|
||||
-- which will replace old_file.
|
||||
-- @return boolean or (nil, string): True if succeeded, or nil and
|
||||
-- an error message.
|
||||
function unix.replace_file(old_file, new_file)
|
||||
return os.rename(new_file, old_file)
|
||||
end
|
||||
|
||||
function unix.tmpname()
|
||||
return os.tmpname()
|
||||
end
|
||||
|
||||
return unix
|
||||
237
Utils/luarocks/lua/luarocks/fs/unix/tools.lua
Normal file
237
Utils/luarocks/lua/luarocks/fs/unix/tools.lua
Normal file
@ -0,0 +1,237 @@
|
||||
|
||||
--- fs operations implemented with third-party tools for Unix platform abstractions.
|
||||
local tools = {}
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
local dir = require("luarocks.dir")
|
||||
local cfg = require("luarocks.cfg")
|
||||
|
||||
local vars = cfg.variables
|
||||
|
||||
--- Adds prefix to command to make it run from a directory.
|
||||
-- @param directory string: Path to a directory.
|
||||
-- @param cmd string: A command-line string.
|
||||
-- @return string: The command-line with prefix.
|
||||
function tools.command_at(directory, cmd)
|
||||
return "cd " .. fs.Q(fs.absolute_name(directory)) .. " && " .. cmd
|
||||
end
|
||||
|
||||
--- Create a directory if it does not already exist.
|
||||
-- If any of the higher levels in the path name does not exist
|
||||
-- too, they are created as well.
|
||||
-- @param directory string: pathname of directory to create.
|
||||
-- @return boolean: true on success, false on failure.
|
||||
function tools.make_dir(directory)
|
||||
assert(directory)
|
||||
local ok, err = fs.execute(vars.MKDIR.." -p", directory)
|
||||
if not ok then
|
||||
err = "failed making directory "..directory
|
||||
end
|
||||
return ok, err
|
||||
end
|
||||
|
||||
--- Remove a directory if it is empty.
|
||||
-- Does not return errors (for example, if directory is not empty or
|
||||
-- if already does not exist)
|
||||
-- @param directory string: pathname of directory to remove.
|
||||
function tools.remove_dir_if_empty(directory)
|
||||
assert(directory)
|
||||
fs.execute_quiet(vars.RMDIR, directory)
|
||||
end
|
||||
|
||||
--- Remove a directory if it is empty.
|
||||
-- Does not return errors (for example, if directory is not empty or
|
||||
-- if already does not exist)
|
||||
-- @param directory string: pathname of directory to remove.
|
||||
function tools.remove_dir_tree_if_empty(directory)
|
||||
assert(directory)
|
||||
fs.execute_quiet(vars.RMDIR, "-p", directory)
|
||||
end
|
||||
|
||||
--- Copy a file.
|
||||
-- @param src string: Pathname of source
|
||||
-- @param dest string: Pathname of destination
|
||||
-- @param perm string or nil: Permissions for destination file,
|
||||
-- @return boolean or (boolean, string): true on success, false on failure,
|
||||
-- plus an error message.
|
||||
function tools.copy(src, dest, perm)
|
||||
assert(src and dest)
|
||||
if fs.execute(vars.CP, src, dest) then
|
||||
if perm then
|
||||
if fs.is_dir(dest) then
|
||||
dest = dir.path(dest, dir.base_name(src))
|
||||
end
|
||||
if fs.chmod(dest, perm) then
|
||||
return true
|
||||
else
|
||||
return false, "Failed setting permissions of "..dest
|
||||
end
|
||||
end
|
||||
return true
|
||||
else
|
||||
return false, "Failed copying "..src.." to "..dest
|
||||
end
|
||||
end
|
||||
|
||||
--- Recursively copy the contents of a directory.
|
||||
-- @param src string: Pathname of source
|
||||
-- @param dest string: Pathname of destination
|
||||
-- @return boolean or (boolean, string): true on success, false on failure,
|
||||
-- plus an error message.
|
||||
function tools.copy_contents(src, dest)
|
||||
assert(src and dest)
|
||||
if fs.execute_quiet(vars.CP.." -pPR "..fs.Q(src).."/* "..fs.Q(dest)) then
|
||||
return true
|
||||
else
|
||||
return false, "Failed copying "..src.." to "..dest
|
||||
end
|
||||
end
|
||||
--- Delete a file or a directory and all its contents.
|
||||
-- For safety, this only accepts absolute paths.
|
||||
-- @param arg string: Pathname of source
|
||||
-- @return nil
|
||||
function tools.delete(arg)
|
||||
assert(arg)
|
||||
assert(arg:sub(1,1) == "/")
|
||||
fs.execute_quiet(vars.RM, "-rf", arg)
|
||||
end
|
||||
|
||||
--- Recursively scan the contents of a directory.
|
||||
-- @param at string or nil: directory to scan (will be the current
|
||||
-- directory if none is given).
|
||||
-- @return table: an array of strings with the filenames representing
|
||||
-- the contents of a directory.
|
||||
function tools.find(at)
|
||||
assert(type(at) == "string" or not at)
|
||||
if not at then
|
||||
at = fs.current_dir()
|
||||
end
|
||||
if not fs.is_dir(at) then
|
||||
return {}
|
||||
end
|
||||
local result = {}
|
||||
local pipe = io.popen(fs.command_at(at, fs.quiet_stderr(vars.FIND.." *")))
|
||||
for file in pipe:lines() do
|
||||
table.insert(result, file)
|
||||
end
|
||||
pipe:close()
|
||||
return result
|
||||
end
|
||||
|
||||
--- Compress files in a .zip archive.
|
||||
-- @param zipfile string: pathname of .zip archive to be created.
|
||||
-- @param ... Filenames to be stored in the archive are given as
|
||||
-- additional arguments.
|
||||
-- @return boolean: true on success, false on failure.
|
||||
function tools.zip(zipfile, ...)
|
||||
return fs.execute(vars.ZIP.." -r", zipfile, ...)
|
||||
end
|
||||
|
||||
--- Uncompress files from a .zip archive.
|
||||
-- @param zipfile string: pathname of .zip archive to be extracted.
|
||||
-- @return boolean: true on success, false on failure.
|
||||
function tools.unzip(zipfile)
|
||||
assert(zipfile)
|
||||
return fs.execute_quiet(vars.UNZIP, zipfile)
|
||||
end
|
||||
|
||||
--- Test is file/directory exists
|
||||
-- @param file string: filename to test
|
||||
-- @return boolean: true if file exists, false otherwise.
|
||||
function tools.exists(file)
|
||||
assert(file)
|
||||
return fs.execute(vars.TEST, "-e", file)
|
||||
end
|
||||
|
||||
--- Test is pathname is a directory.
|
||||
-- @param file string: pathname to test
|
||||
-- @return boolean: true if it is a directory, false otherwise.
|
||||
function tools.is_dir(file)
|
||||
assert(file)
|
||||
return fs.execute(vars.TEST, "-d", file)
|
||||
end
|
||||
|
||||
--- Test is pathname is a regular file.
|
||||
-- @param file string: pathname to test
|
||||
-- @return boolean: true if it is a regular file, false otherwise.
|
||||
function tools.is_file(file)
|
||||
assert(file)
|
||||
return fs.execute(vars.TEST, "-f", file)
|
||||
end
|
||||
|
||||
function tools.chmod(pathname, mode)
|
||||
if mode then
|
||||
return fs.execute(vars.CHMOD, mode, pathname)
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- Unpack an archive.
|
||||
-- Extract the contents of an archive, detecting its format by
|
||||
-- filename extension.
|
||||
-- @param archive string: Filename of archive.
|
||||
-- @return boolean or (boolean, string): true on success, false and an error message on failure.
|
||||
function tools.unpack_archive(archive)
|
||||
assert(type(archive) == "string")
|
||||
|
||||
local pipe_to_tar = " | "..vars.TAR.." -xf -"
|
||||
|
||||
if not cfg.verbose then
|
||||
pipe_to_tar = " 2> /dev/null"..fs.quiet(pipe_to_tar)
|
||||
end
|
||||
|
||||
local ok
|
||||
if archive:match("%.tar%.gz$") or archive:match("%.tgz$") then
|
||||
ok = fs.execute_string(vars.GUNZIP.." -c "..fs.Q(archive)..pipe_to_tar)
|
||||
elseif archive:match("%.tar%.bz2$") then
|
||||
ok = fs.execute_string(vars.BUNZIP2.." -c "..fs.Q(archive)..pipe_to_tar)
|
||||
elseif archive:match("%.zip$") then
|
||||
ok = fs.execute_quiet(vars.UNZIP, archive)
|
||||
elseif archive:match("%.lua$") or archive:match("%.c$") then
|
||||
-- Ignore .lua and .c files; they don't need to be extracted.
|
||||
return true
|
||||
else
|
||||
return false, "Couldn't extract archive "..archive..": unrecognized filename extension"
|
||||
end
|
||||
if not ok then
|
||||
return false, "Failed extracting "..archive
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function tools.get_permissions(filename)
|
||||
local pipe = io.popen(vars.STAT.." "..vars.STATFLAG.." "..fs.Q(filename))
|
||||
local ret = pipe:read("*l")
|
||||
pipe:close()
|
||||
return ret
|
||||
end
|
||||
|
||||
function tools.browser(url)
|
||||
return fs.execute(cfg.web_browser, url)
|
||||
end
|
||||
|
||||
function tools.set_time(file, time)
|
||||
file = dir.normalize(file)
|
||||
return fs.execute(vars.TOUCH, "-d", "@"..tostring(time), file)
|
||||
end
|
||||
|
||||
--- Create a temporary directory.
|
||||
-- @param name string: name pattern to use for avoiding conflicts
|
||||
-- when creating temporary directory.
|
||||
-- @return string or (nil, string): name of temporary directory or (nil, error message) on failure.
|
||||
function tools.make_temp_dir(name)
|
||||
assert(type(name) == "string")
|
||||
name = dir.normalize(name)
|
||||
|
||||
local template = (os.getenv("TMPDIR") or "/tmp") .. "/luarocks_" .. name:gsub(dir.separator, "_") .. "-XXXXXX"
|
||||
local pipe = io.popen(vars.MKTEMP.." -d "..fs.Q(template))
|
||||
local dirname = pipe:read("*l")
|
||||
pipe:close()
|
||||
if dirname and dirname:match("^/") then
|
||||
return dirname
|
||||
end
|
||||
return nil, "Failed to create temporary directory "..tostring(dirname)
|
||||
end
|
||||
|
||||
return tools
|
||||
266
Utils/luarocks/lua/luarocks/fs/win32.lua
Normal file
266
Utils/luarocks/lua/luarocks/fs/win32.lua
Normal file
@ -0,0 +1,266 @@
|
||||
--- Windows implementation of filesystem and platform abstractions.
|
||||
-- Download http://unxutils.sourceforge.net/ for Windows GNU utilities
|
||||
-- used by this module.
|
||||
local win32 = {}
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
|
||||
local cfg = require("luarocks.cfg")
|
||||
local dir = require("luarocks.dir")
|
||||
local util = require("luarocks.util")
|
||||
|
||||
math.randomseed(os.time())
|
||||
|
||||
-- Monkey patch io.popen and os.execute to make sure quoting
|
||||
-- works as expected.
|
||||
-- See http://lua-users.org/lists/lua-l/2013-11/msg00367.html
|
||||
local _prefix = "type NUL && "
|
||||
local _popen, _execute = io.popen, os.execute
|
||||
io.popen = function(cmd, ...) return _popen(_prefix..cmd, ...) end
|
||||
os.execute = function(cmd, ...) return _execute(_prefix..cmd, ...) end
|
||||
|
||||
--- Annotate command string for quiet execution.
|
||||
-- @param cmd string: A command-line string.
|
||||
-- @return string: The command-line, with silencing annotation.
|
||||
function win32.quiet(cmd)
|
||||
return cmd.." 2> NUL 1> NUL"
|
||||
end
|
||||
|
||||
--- Annotate command string for execution with quiet stderr.
|
||||
-- @param cmd string: A command-line string.
|
||||
-- @return string: The command-line, with stderr silencing annotation.
|
||||
function win32.quiet_stderr(cmd)
|
||||
return cmd.." 2> NUL"
|
||||
end
|
||||
|
||||
-- Split path into root and the rest.
|
||||
-- Root part consists of an optional drive letter (e.g. "C:")
|
||||
-- and an optional directory separator.
|
||||
local function split_root(path)
|
||||
local root = ""
|
||||
|
||||
if path:match("^.:") then
|
||||
root = path:sub(1, 2)
|
||||
path = path:sub(3)
|
||||
end
|
||||
|
||||
if path:match("^[\\/]") then
|
||||
root = path:sub(1, 1)
|
||||
path = path:sub(2)
|
||||
end
|
||||
|
||||
return root, path
|
||||
end
|
||||
|
||||
--- Quote argument for shell processing. Fixes paths on Windows.
|
||||
-- Adds double quotes and escapes.
|
||||
-- @param arg string: Unquoted argument.
|
||||
-- @return string: Quoted argument.
|
||||
function win32.Q(arg)
|
||||
assert(type(arg) == "string")
|
||||
-- Use Windows-specific directory separator for paths.
|
||||
-- Paths should be converted to absolute by now.
|
||||
if split_root(arg) ~= "" then
|
||||
arg = arg:gsub("/", "\\")
|
||||
end
|
||||
if arg == "\\" then
|
||||
return '\\' -- CHDIR needs special handling for root dir
|
||||
end
|
||||
-- URLs and anything else
|
||||
arg = arg:gsub('\\(\\*)"', '\\%1%1"')
|
||||
arg = arg:gsub('\\+$', '%0%0')
|
||||
arg = arg:gsub('"', '\\"')
|
||||
arg = arg:gsub('(\\*)%%', '%1%1"%%"')
|
||||
return '"' .. arg .. '"'
|
||||
end
|
||||
|
||||
--- Quote argument for shell processing in batch files.
|
||||
-- Adds double quotes and escapes.
|
||||
-- @param arg string: Unquoted argument.
|
||||
-- @return string: Quoted argument.
|
||||
function win32.Qb(arg)
|
||||
assert(type(arg) == "string")
|
||||
-- Use Windows-specific directory separator for paths.
|
||||
-- Paths should be converted to absolute by now.
|
||||
if split_root(arg) ~= "" then
|
||||
arg = arg:gsub("/", "\\")
|
||||
end
|
||||
if arg == "\\" then
|
||||
return '\\' -- CHDIR needs special handling for root dir
|
||||
end
|
||||
-- URLs and anything else
|
||||
arg = arg:gsub('\\(\\*)"', '\\%1%1"')
|
||||
arg = arg:gsub('\\+$', '%0%0')
|
||||
arg = arg:gsub('"', '\\"')
|
||||
arg = arg:gsub('%%', '%%%%')
|
||||
return '"' .. arg .. '"'
|
||||
end
|
||||
|
||||
--- Return an absolute pathname from a potentially relative one.
|
||||
-- @param pathname string: pathname to convert.
|
||||
-- @param relative_to string or nil: path to prepend when making
|
||||
-- pathname absolute, or the current dir in the dir stack if
|
||||
-- not given.
|
||||
-- @return string: The pathname converted to absolute.
|
||||
function win32.absolute_name(pathname, relative_to)
|
||||
assert(type(pathname) == "string")
|
||||
assert(type(relative_to) == "string" or not relative_to)
|
||||
|
||||
relative_to = relative_to or fs.current_dir()
|
||||
local root, rest = split_root(pathname)
|
||||
if root:match("[\\/]$") then
|
||||
-- It's an absolute path already.
|
||||
return pathname
|
||||
else
|
||||
-- It's a relative path, join it with base path.
|
||||
-- This drops drive letter from paths like "C:foo".
|
||||
return relative_to .. "/" .. rest
|
||||
end
|
||||
end
|
||||
|
||||
--- Return the root directory for the given path.
|
||||
-- For example, for "c:\hello", returns "c:\"
|
||||
-- @param pathname string: pathname to use.
|
||||
-- @return string: The root of the given pathname.
|
||||
function win32.root_of(pathname)
|
||||
return (split_root(fs.absolute_name(pathname)))
|
||||
end
|
||||
|
||||
--- Create a wrapper to make a script executable from the command-line.
|
||||
-- @param file string: Pathname of script to be made executable.
|
||||
-- @param dest string: Directory where to put the wrapper.
|
||||
-- @param name string: rock name to be used in loader context.
|
||||
-- @param version string: rock version to be used in loader context.
|
||||
-- @return boolean or (nil, string): True if succeeded, or nil and
|
||||
-- an error message.
|
||||
function win32.wrap_script(file, dest, name, version)
|
||||
assert(type(file) == "string")
|
||||
assert(type(dest) == "string")
|
||||
|
||||
local base = dir.base_name(file)
|
||||
local wrapname = fs.is_dir(dest) and dest.."/"..base or dest
|
||||
wrapname = wrapname..".bat"
|
||||
local lpath, lcpath = cfg.package_paths()
|
||||
lpath = util.remove_path_dupes(lpath, ";")
|
||||
lcpath = util.remove_path_dupes(lcpath, ";")
|
||||
local wrapper = io.open(wrapname, "w")
|
||||
if not wrapper then
|
||||
return nil, "Could not open "..wrapname.." for writing."
|
||||
end
|
||||
wrapper:write("@echo off\n")
|
||||
local lua = dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)
|
||||
local ppaths = "package.path="..util.LQ(lpath..";").."..package.path; package.cpath="..util.LQ(lcpath..";").."..package.cpath"
|
||||
local addctx = "local k,l,_=pcall(require,"..util.LQ("luarocks.loader")..") _=k and l.add_context("..util.LQ(name)..","..util.LQ(version)..")"
|
||||
wrapper:write(fs.Qb(lua)..' -e '..fs.Qb(ppaths)..' -e '..fs.Qb(addctx)..' '..fs.Qb(file)..' %*\n')
|
||||
wrapper:write("exit /b %ERRORLEVEL%\n")
|
||||
wrapper:close()
|
||||
return true
|
||||
end
|
||||
|
||||
function win32.is_actual_binary(name)
|
||||
name = name:lower()
|
||||
if name:match("%.bat$") or name:match("%.exe$") then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function win32.copy_binary(filename, dest)
|
||||
local ok, err = fs.copy(filename, dest)
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
local exe_pattern = "%.[Ee][Xx][Ee]$"
|
||||
local base = dir.base_name(filename)
|
||||
dest = dir.dir_name(dest)
|
||||
if base:match(exe_pattern) then
|
||||
base = base:gsub(exe_pattern, ".lua")
|
||||
local helpname = dest.."/"..base
|
||||
local helper = io.open(helpname, "w")
|
||||
if not helper then
|
||||
return nil, "Could not open "..helpname.." for writing."
|
||||
end
|
||||
helper:write('package.path=\"'..package.path:gsub("\\","\\\\")..';\"..package.path\n')
|
||||
helper:write('package.cpath=\"'..package.path:gsub("\\","\\\\")..';\"..package.cpath\n')
|
||||
helper:close()
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function win32.chmod(filename, mode)
|
||||
return true
|
||||
end
|
||||
|
||||
function win32.get_permissions(filename)
|
||||
return ""
|
||||
end
|
||||
|
||||
--- Move a file on top of the other.
|
||||
-- The new file ceases to exist under its original name,
|
||||
-- and takes over the name of the old file.
|
||||
-- On Windows this is done by removing the original file and
|
||||
-- renaming the new file to its original name.
|
||||
-- @param old_file The name of the original file,
|
||||
-- which will be the new name of new_file.
|
||||
-- @param new_file The name of the new file,
|
||||
-- which will replace old_file.
|
||||
-- @return boolean or (nil, string): True if succeeded, or nil and
|
||||
-- an error message.
|
||||
function win32.replace_file(old_file, new_file)
|
||||
os.remove(old_file)
|
||||
return os.rename(new_file, old_file)
|
||||
end
|
||||
|
||||
--- Test is file/dir is writable.
|
||||
-- Warning: testing if a file/dir is writable does not guarantee
|
||||
-- that it will remain writable and therefore it is no replacement
|
||||
-- for checking the result of subsequent operations.
|
||||
-- @param file string: filename to test
|
||||
-- @return boolean: true if file exists, false otherwise.
|
||||
function win32.is_writable(file)
|
||||
assert(file)
|
||||
file = dir.normalize(file)
|
||||
local result
|
||||
local tmpname = 'tmpluarockstestwritable.deleteme'
|
||||
if fs.is_dir(file) then
|
||||
local file2 = dir.path(file, tmpname)
|
||||
local fh = io.open(file2, 'wb')
|
||||
result = fh ~= nil
|
||||
if fh then fh:close() end
|
||||
if result then
|
||||
-- the above test might give a false positive when writing to
|
||||
-- c:\program files\ because of VirtualStore redirection on Vista and up
|
||||
-- So check whether it's really there
|
||||
result = fs.exists(file2)
|
||||
end
|
||||
os.remove(file2)
|
||||
else
|
||||
local fh = io.open(file, 'r+b')
|
||||
result = fh ~= nil
|
||||
if fh then fh:close() end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
--- Create a temporary directory.
|
||||
-- @param name string: name pattern to use for avoiding conflicts
|
||||
-- when creating temporary directory.
|
||||
-- @return string or (nil, string): name of temporary directory or (nil, error message) on failure.
|
||||
function win32.make_temp_dir(name)
|
||||
assert(type(name) == "string")
|
||||
name = dir.normalize(name)
|
||||
|
||||
local temp_dir = os.getenv("TMP") .. "/luarocks_" .. name:gsub(dir.separator, "_") .. "-" .. tostring(math.floor(math.random() * 10000))
|
||||
local ok, err = fs.make_dir(temp_dir)
|
||||
if ok then
|
||||
return temp_dir
|
||||
else
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
|
||||
function win32.tmpname()
|
||||
return os.getenv("TMP")..os.tmpname()
|
||||
end
|
||||
|
||||
return win32
|
||||
227
Utils/luarocks/lua/luarocks/fs/win32/tools.lua
Normal file
227
Utils/luarocks/lua/luarocks/fs/win32/tools.lua
Normal file
@ -0,0 +1,227 @@
|
||||
|
||||
--- fs operations implemented with third-party tools for Windows platform abstractions.
|
||||
-- Download http://unxutils.sourceforge.net/ for Windows GNU utilities
|
||||
-- used by this module.
|
||||
local tools = {}
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
local dir = require("luarocks.dir")
|
||||
local cfg = require("luarocks.cfg")
|
||||
|
||||
local vars = cfg.variables
|
||||
|
||||
--- Adds prefix to command to make it run from a directory.
|
||||
-- @param directory string: Path to a directory.
|
||||
-- @param cmd string: A command-line string.
|
||||
-- @return string: The command-line with prefix.
|
||||
function tools.command_at(directory, cmd)
|
||||
local drive = directory:match("^([A-Za-z]:)")
|
||||
cmd = "cd " .. fs.Q(directory) .. " & " .. cmd
|
||||
if drive then
|
||||
cmd = drive .. " & " .. cmd
|
||||
end
|
||||
return cmd
|
||||
end
|
||||
|
||||
--- Create a directory if it does not already exist.
|
||||
-- If any of the higher levels in the path name does not exist
|
||||
-- too, they are created as well.
|
||||
-- @param directory string: pathname of directory to create.
|
||||
-- @return boolean: true on success, false on failure.
|
||||
function tools.make_dir(directory)
|
||||
assert(directory)
|
||||
directory = dir.normalize(directory)
|
||||
fs.execute_quiet(fs.Q(vars.MKDIR).." -p ", directory)
|
||||
if not fs.is_dir(directory) then
|
||||
return false, "failed making directory "..directory
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Remove a directory if it is empty.
|
||||
-- Does not return errors (for example, if directory is not empty or
|
||||
-- if already does not exist)
|
||||
-- @param directory string: pathname of directory to remove.
|
||||
function tools.remove_dir_if_empty(directory)
|
||||
assert(directory)
|
||||
fs.execute_quiet(fs.Q(vars.RMDIR), directory)
|
||||
end
|
||||
|
||||
--- Remove a directory if it is empty.
|
||||
-- Does not return errors (for example, if directory is not empty or
|
||||
-- if already does not exist)
|
||||
-- @param directory string: pathname of directory to remove.
|
||||
function tools.remove_dir_tree_if_empty(directory)
|
||||
assert(directory)
|
||||
fs.execute_quiet(fs.Q(vars.RMDIR), directory)
|
||||
end
|
||||
|
||||
--- Copy a file.
|
||||
-- @param src string: Pathname of source
|
||||
-- @param dest string: Pathname of destination
|
||||
-- @return boolean or (boolean, string): true on success, false on failure,
|
||||
-- plus an error message.
|
||||
function tools.copy(src, dest)
|
||||
assert(src and dest)
|
||||
if dest:match("[/\\]$") then dest = dest:sub(1, -2) end
|
||||
local ok = fs.execute(fs.Q(vars.CP), src, dest)
|
||||
if ok then
|
||||
return true
|
||||
else
|
||||
return false, "Failed copying "..src.." to "..dest
|
||||
end
|
||||
end
|
||||
|
||||
--- Recursively copy the contents of a directory.
|
||||
-- @param src string: Pathname of source
|
||||
-- @param dest string: Pathname of destination
|
||||
-- @return boolean or (boolean, string): true on success, false on failure,
|
||||
-- plus an error message.
|
||||
function tools.copy_contents(src, dest)
|
||||
assert(src and dest)
|
||||
if fs.execute_quiet(fs.Q(vars.CP), "-dR", src.."\\*.*", dest) then
|
||||
return true
|
||||
else
|
||||
return false, "Failed copying "..src.." to "..dest
|
||||
end
|
||||
end
|
||||
|
||||
--- Delete a file or a directory and all its contents.
|
||||
-- For safety, this only accepts absolute paths.
|
||||
-- @param arg string: Pathname of source
|
||||
-- @return nil
|
||||
function tools.delete(arg)
|
||||
assert(arg)
|
||||
assert(arg:match("^[a-zA-Z]?:?[\\/]"))
|
||||
fs.execute_quiet("if exist "..fs.Q(arg.."\\").." ( RMDIR /S /Q "..fs.Q(arg).." ) else ( DEL /Q /F "..fs.Q(arg).." )")
|
||||
end
|
||||
|
||||
--- Recursively scan the contents of a directory.
|
||||
-- @param at string or nil: directory to scan (will be the current
|
||||
-- directory if none is given).
|
||||
-- @return table: an array of strings with the filenames representing
|
||||
-- the contents of a directory. Paths are returned with forward slashes.
|
||||
function tools.find(at)
|
||||
assert(type(at) == "string" or not at)
|
||||
if not at then
|
||||
at = fs.current_dir()
|
||||
end
|
||||
if not fs.is_dir(at) then
|
||||
return {}
|
||||
end
|
||||
local result = {}
|
||||
local pipe = io.popen(fs.command_at(at, fs.quiet_stderr(fs.Q(vars.FIND))))
|
||||
for file in pipe:lines() do
|
||||
-- Windows find is a bit different
|
||||
local first_two = file:sub(1,2)
|
||||
if first_two == ".\\" or first_two == "./" then file=file:sub(3) end
|
||||
if file ~= "." then
|
||||
table.insert(result, (file:gsub("\\", "/")))
|
||||
end
|
||||
end
|
||||
pipe:close()
|
||||
return result
|
||||
end
|
||||
|
||||
--- Compress files in a .zip archive.
|
||||
-- @param zipfile string: pathname of .zip archive to be created.
|
||||
-- @param ... Filenames to be stored in the archive are given as
|
||||
-- additional arguments.
|
||||
-- @return boolean: true on success, false on failure.
|
||||
function tools.zip(zipfile, ...)
|
||||
return fs.execute_quiet(fs.Q(vars.SEVENZ).." -aoa a -tzip", zipfile, ...)
|
||||
end
|
||||
|
||||
--- Uncompress files from a .zip archive.
|
||||
-- @param zipfile string: pathname of .zip archive to be extracted.
|
||||
-- @return boolean: true on success, false on failure.
|
||||
function tools.unzip(zipfile)
|
||||
assert(zipfile)
|
||||
return fs.execute_quiet(fs.Q(vars.SEVENZ).." -aoa x", zipfile)
|
||||
end
|
||||
|
||||
--- Test is pathname is a directory.
|
||||
-- @param file string: pathname to test
|
||||
-- @return boolean: true if it is a directory, false otherwise.
|
||||
function tools.is_dir(file)
|
||||
assert(file)
|
||||
return fs.execute_quiet("if not exist " .. fs.Q(file.."\\").." invalidcommandname")
|
||||
end
|
||||
|
||||
--- Test is pathname is a regular file.
|
||||
-- @param file string: pathname to test
|
||||
-- @return boolean: true if it is a regular file, false otherwise.
|
||||
function tools.is_file(file)
|
||||
assert(file)
|
||||
return fs.execute(fs.Q(vars.TEST).." -f", file)
|
||||
end
|
||||
|
||||
--- Strip the last extension of a filename.
|
||||
-- Example: "foo.tar.gz" becomes "foo.tar".
|
||||
-- If filename has no dots, returns it unchanged.
|
||||
-- @param filename string: The file name to strip.
|
||||
-- @return string: The stripped name.
|
||||
local function strip_extension(filename)
|
||||
assert(type(filename) == "string")
|
||||
return (filename:gsub("%.[^.]+$", "")) or filename
|
||||
end
|
||||
|
||||
--- Uncompress gzip file.
|
||||
-- @param archive string: Filename of archive.
|
||||
-- @return boolean : success status
|
||||
local function gunzip(archive)
|
||||
return fs.execute_quiet(fs.Q(vars.SEVENZ).." -aoa x", archive)
|
||||
end
|
||||
|
||||
--- Unpack an archive.
|
||||
-- Extract the contents of an archive, detecting its format by
|
||||
-- filename extension.
|
||||
-- @param archive string: Filename of archive.
|
||||
-- @return boolean or (boolean, string): true on success, false and an error message on failure.
|
||||
function tools.unpack_archive(archive)
|
||||
assert(type(archive) == "string")
|
||||
|
||||
local ok
|
||||
local sevenzx = fs.Q(vars.SEVENZ).." -aoa x"
|
||||
if archive:match("%.tar%.gz$") then
|
||||
ok = gunzip(archive)
|
||||
if ok then
|
||||
ok = fs.execute_quiet(sevenzx, strip_extension(archive))
|
||||
end
|
||||
elseif archive:match("%.tgz$") then
|
||||
ok = gunzip(archive)
|
||||
if ok then
|
||||
ok = fs.execute_quiet(sevenzx, strip_extension(archive)..".tar")
|
||||
end
|
||||
elseif archive:match("%.tar%.bz2$") then
|
||||
ok = fs.execute_quiet(sevenzx, archive)
|
||||
if ok then
|
||||
ok = fs.execute_quiet(sevenzx, strip_extension(archive))
|
||||
end
|
||||
elseif archive:match("%.zip$") then
|
||||
ok = fs.execute_quiet(sevenzx, archive)
|
||||
elseif archive:match("%.lua$") or archive:match("%.c$") then
|
||||
-- Ignore .lua and .c files; they don't need to be extracted.
|
||||
return true
|
||||
else
|
||||
return false, "Couldn't extract archive "..archive..": unrecognized filename extension"
|
||||
end
|
||||
if not ok then
|
||||
return false, "Failed extracting "..archive
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Test for existance of a file.
|
||||
-- @param file string: filename to test
|
||||
-- @return boolean: true if file exists, false otherwise.
|
||||
function tools.exists(file)
|
||||
assert(file)
|
||||
return fs.execute_quiet("if not exist " .. fs.Q(file) .. " invalidcommandname")
|
||||
end
|
||||
|
||||
function tools.browser(url)
|
||||
return fs.execute(cfg.web_browser..' "Starting docs..." '..fs.Q(url))
|
||||
end
|
||||
|
||||
return tools
|
||||
118
Utils/luarocks/lua/luarocks/help.lua
Normal file
118
Utils/luarocks/lua/luarocks/help.lua
Normal file
@ -0,0 +1,118 @@
|
||||
|
||||
--- Module implementing the LuaRocks "help" command.
|
||||
-- This is a generic help display module, which
|
||||
-- uses a global table called "commands" to find commands
|
||||
-- to show help for; each command should be represented by a
|
||||
-- table containing "help" and "help_summary" fields.
|
||||
local help = {}
|
||||
|
||||
local util = require("luarocks.util")
|
||||
local cfg = require("luarocks.cfg")
|
||||
local dir = require("luarocks.dir")
|
||||
|
||||
local program = util.this_program("luarocks")
|
||||
|
||||
util.add_run_function(help)
|
||||
help.help_summary = "Help on commands. Type '"..program.." help <command>' for more."
|
||||
|
||||
help.help_arguments = "[<command>]"
|
||||
help.help = [[
|
||||
<command> is the command to show help for.
|
||||
]]
|
||||
|
||||
local function print_banner()
|
||||
util.printout("\nLuaRocks "..cfg.program_version..", a module deployment system for Lua")
|
||||
end
|
||||
|
||||
local function print_section(section)
|
||||
util.printout("\n"..section)
|
||||
end
|
||||
|
||||
local function get_status(status)
|
||||
if status then
|
||||
return "ok"
|
||||
else
|
||||
return "not found"
|
||||
end
|
||||
end
|
||||
|
||||
--- Driver function for the "help" command.
|
||||
-- @param command string or nil: command to show help for; if not
|
||||
-- given, help summaries for all commands are shown.
|
||||
-- @return boolean or (nil, string): true if there were no errors
|
||||
-- or nil and an error message if an invalid command was requested.
|
||||
function help.command(flags, command)
|
||||
if not command then
|
||||
local conf = cfg.which_config()
|
||||
print_banner()
|
||||
print_section("NAME")
|
||||
util.printout("\t"..program..[[ - ]]..program_description)
|
||||
print_section("SYNOPSIS")
|
||||
util.printout("\t"..program..[[ [--from=<server> | --only-from=<server>] [--to=<tree>] [VAR=VALUE]... <command> [<argument>] ]])
|
||||
print_section("GENERAL OPTIONS")
|
||||
util.printout([[
|
||||
These apply to all commands, as appropriate:
|
||||
|
||||
--server=<server> Fetch rocks/rockspecs from this server
|
||||
(takes priority over config file)
|
||||
--only-server=<server> Fetch rocks/rockspecs from this server only
|
||||
(overrides any entries in the config file)
|
||||
--only-sources=<url> Restrict downloads to paths matching the
|
||||
given URL.
|
||||
--tree=<tree> Which tree to operate on.
|
||||
--local Use the tree in the user's home directory.
|
||||
To enable it, see ']]..program..[[ help path'.
|
||||
--verbose Display verbose output of commands executed.
|
||||
--timeout=<seconds> Timeout on network operations, in seconds.
|
||||
0 means no timeout (wait forever).
|
||||
Default is ]]..tostring(cfg.connection_timeout)..[[.]])
|
||||
print_section("VARIABLES")
|
||||
util.printout([[
|
||||
Variables from the "variables" table of the configuration file
|
||||
can be overriden with VAR=VALUE assignments.]])
|
||||
print_section("COMMANDS")
|
||||
for name, command in util.sortedpairs(commands) do
|
||||
local cmd = require(command)
|
||||
util.printout("", name)
|
||||
util.printout("\t", cmd.help_summary)
|
||||
end
|
||||
print_section("CONFIGURATION")
|
||||
util.printout("\tLua version: " .. cfg.lua_version)
|
||||
util.printout("\tConfiguration files:")
|
||||
util.printout("\t\tSystem: ".. dir.normalize(conf.system.file) .. " (" .. get_status(conf.system.ok) ..")")
|
||||
if conf.user.file then
|
||||
util.printout("\t\tUser : ".. dir.normalize(conf.user.file) .. " (" .. get_status(conf.user.ok) ..")\n")
|
||||
else
|
||||
util.printout("\t\tUser : disabled in this LuaRocks installation.\n")
|
||||
end
|
||||
util.printout("\tRocks trees in use: ")
|
||||
for _, tree in ipairs(cfg.rocks_trees) do
|
||||
if type(tree) == "string" then
|
||||
util.printout("\t\t"..dir.normalize(tree))
|
||||
else
|
||||
local name = tree.name and " (\""..tree.name.."\")" or ""
|
||||
util.printout("\t\t"..dir.normalize(tree.root)..name)
|
||||
end
|
||||
end
|
||||
else
|
||||
command = command:gsub("-", "_")
|
||||
local cmd = commands[command] and require(commands[command])
|
||||
if cmd then
|
||||
local arguments = cmd.help_arguments or "<argument>"
|
||||
print_banner()
|
||||
print_section("NAME")
|
||||
util.printout("\t"..program.." "..command.." - "..cmd.help_summary)
|
||||
print_section("SYNOPSIS")
|
||||
util.printout("\t"..program.." "..command.." "..arguments)
|
||||
print_section("DESCRIPTION")
|
||||
util.printout("",(cmd.help:gsub("\n","\n\t"):gsub("\n\t$","")))
|
||||
print_section("SEE ALSO")
|
||||
util.printout("","'"..program.." help' for general options and configuration.\n")
|
||||
else
|
||||
return nil, "Unknown command: "..command
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return help
|
||||
186
Utils/luarocks/lua/luarocks/index.lua
Normal file
186
Utils/luarocks/lua/luarocks/index.lua
Normal file
@ -0,0 +1,186 @@
|
||||
|
||||
--- Module which builds the index.html page to be used in rocks servers.
|
||||
local index = {}
|
||||
package.loaded["luarocks.index"] = index
|
||||
|
||||
local util = require("luarocks.util")
|
||||
local fs = require("luarocks.fs")
|
||||
local deps = require("luarocks.deps")
|
||||
local persist = require("luarocks.persist")
|
||||
local dir = require("luarocks.dir")
|
||||
local manif = require("luarocks.manif")
|
||||
|
||||
local ext_url_target = ' target="_blank"'
|
||||
|
||||
local index_header = [[
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>Available rocks</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
|
||||
<style>
|
||||
body {
|
||||
background-color: white;
|
||||
font-family: "bitstream vera sans", "verdana", "sans";
|
||||
font-size: 14px;
|
||||
}
|
||||
a {
|
||||
color: #0000c0;
|
||||
text-decoration: none;
|
||||
}
|
||||
a.pkg {
|
||||
color: black;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
td.main {
|
||||
border-style: none;
|
||||
}
|
||||
blockquote {
|
||||
font-size: 12px;
|
||||
}
|
||||
td.package {
|
||||
background-color: #f0f0f0;
|
||||
vertical-align: top;
|
||||
}
|
||||
td.spacer {
|
||||
height: 5px;
|
||||
}
|
||||
td.version {
|
||||
background-color: #d0d0d0;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
padding: 5px;
|
||||
width: 100px;
|
||||
}
|
||||
p.manifest {
|
||||
font-size: 8px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Available rocks</h1>
|
||||
<p>
|
||||
Lua modules available from this location for use with <a href="http://www.luarocks.org">LuaRocks</a>:
|
||||
</p>
|
||||
<table class="main">
|
||||
]]
|
||||
|
||||
local index_package_begin = [[
|
||||
<td class="package">
|
||||
<p><a name="$anchor"></a><a href="#$anchor" class="pkg"><b>$package</b></a> - $summary<br/>
|
||||
</p><blockquote><p>$detailed<br/>
|
||||
$externaldependencies
|
||||
<font size="-1"><a href="$original">latest sources</a> $homepage | License: $license</font></p>
|
||||
</blockquote></a></td>
|
||||
<td class="version">
|
||||
]]
|
||||
|
||||
local index_package_end = [[
|
||||
</td></tr>
|
||||
<tr><td colspan="2" class="spacer"></td></tr>
|
||||
]]
|
||||
|
||||
local index_footer_begin = [[
|
||||
</table>
|
||||
<p class="manifest">
|
||||
<a href="manifest">manifest file</a>
|
||||
]]
|
||||
local index_manifest_ver = [[
|
||||
• <a href="manifest-$VER">Lua $VER manifest file</a> (<a href="manifest-$VER.zip">zip</a>)
|
||||
]]
|
||||
local index_footer_end = [[
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
]]
|
||||
|
||||
function index.format_external_dependencies(rockspec)
|
||||
if rockspec.external_dependencies then
|
||||
local deplist = {}
|
||||
local listed_set = {}
|
||||
local plats = nil
|
||||
for name, desc in util.sortedpairs(rockspec.external_dependencies) do
|
||||
if name ~= "platforms" then
|
||||
table.insert(deplist, name:lower())
|
||||
listed_set[name] = true
|
||||
else
|
||||
plats = desc
|
||||
end
|
||||
end
|
||||
if plats then
|
||||
for plat, entries in util.sortedpairs(plats) do
|
||||
for name, desc in util.sortedpairs(entries) do
|
||||
if not listed_set[name] then
|
||||
table.insert(deplist, name:lower() .. " (on "..plat..")")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return '<p><b>External dependencies:</b> ' .. table.concat(deplist, ', ').. '</p>'
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
function index.make_index(repo)
|
||||
if not fs.is_dir(repo) then
|
||||
return nil, "Cannot access repository at "..repo
|
||||
end
|
||||
local manifest = manif.load_manifest(repo)
|
||||
local out = io.open(dir.path(repo, "index.html"), "w")
|
||||
|
||||
out:write(index_header)
|
||||
for package, version_list in util.sortedpairs(manifest.repository) do
|
||||
local latest_rockspec = nil
|
||||
local output = index_package_begin
|
||||
for version, data in util.sortedpairs(version_list, deps.compare_versions) do
|
||||
local versions = {}
|
||||
output = output..version..': '
|
||||
table.sort(data, function(a,b) return a.arch < b.arch end)
|
||||
for _, item in ipairs(data) do
|
||||
local file
|
||||
if item.arch == 'rockspec' then
|
||||
file = ("%s-%s.rockspec"):format(package, version)
|
||||
if not latest_rockspec then latest_rockspec = file end
|
||||
else
|
||||
file = ("%s-%s.%s.rock"):format(package, version, item.arch)
|
||||
end
|
||||
table.insert(versions, '<a href="'..file..'">'..item.arch..'</a>')
|
||||
end
|
||||
output = output .. table.concat(versions, ', ') .. '<br/>'
|
||||
end
|
||||
output = output .. index_package_end
|
||||
if latest_rockspec then
|
||||
local rockspec = persist.load_into_table(dir.path(repo, latest_rockspec))
|
||||
local descript = rockspec.description or {}
|
||||
local vars = {
|
||||
anchor = package,
|
||||
package = rockspec.package,
|
||||
original = rockspec.source.url,
|
||||
summary = descript.summary or "",
|
||||
detailed = descript.detailed or "",
|
||||
license = descript.license or "N/A",
|
||||
homepage = descript.homepage and ('| <a href="'..descript.homepage..'"'..ext_url_target..'>project homepage</a>') or "",
|
||||
externaldependencies = index.format_external_dependencies(rockspec)
|
||||
}
|
||||
vars.detailed = vars.detailed:gsub("\n\n", "</p><p>"):gsub("%s+", " ")
|
||||
vars.detailed = vars.detailed:gsub("(https?://[a-zA-Z0-9%.%%-_%+%[%]=%?&/$@;:]+)", '<a href="%1"'..ext_url_target..'>%1</a>')
|
||||
output = output:gsub("$(%w+)", vars)
|
||||
else
|
||||
output = output:gsub("$anchor", package)
|
||||
output = output:gsub("$package", package)
|
||||
output = output:gsub("$(%w+)", "")
|
||||
end
|
||||
out:write(output)
|
||||
end
|
||||
out:write(index_footer_begin)
|
||||
for ver in util.lua_versions() do
|
||||
out:write((index_manifest_ver:gsub("$VER", ver)))
|
||||
end
|
||||
out:write(index_footer_end)
|
||||
out:close()
|
||||
end
|
||||
|
||||
return index
|
||||
188
Utils/luarocks/lua/luarocks/install.lua
Normal file
188
Utils/luarocks/lua/luarocks/install.lua
Normal file
@ -0,0 +1,188 @@
|
||||
--- Module implementing the LuaRocks "install" command.
|
||||
-- Installs binary rocks.
|
||||
local install = {}
|
||||
package.loaded["luarocks.install"] = install
|
||||
|
||||
local path = require("luarocks.path")
|
||||
local repos = require("luarocks.repos")
|
||||
local fetch = require("luarocks.fetch")
|
||||
local util = require("luarocks.util")
|
||||
local fs = require("luarocks.fs")
|
||||
local deps = require("luarocks.deps")
|
||||
local manif = require("luarocks.manif")
|
||||
local remove = require("luarocks.remove")
|
||||
local cfg = require("luarocks.cfg")
|
||||
|
||||
util.add_run_function(install)
|
||||
install.help_summary = "Install a rock."
|
||||
|
||||
install.help_arguments = "{<rock>|<name> [<version>]}"
|
||||
|
||||
install.help = [[
|
||||
Argument may be the name of a rock to be fetched from a repository
|
||||
or a filename of a locally available rock.
|
||||
|
||||
--keep Do not remove previously installed versions of the
|
||||
rock after installing a new one. This behavior can
|
||||
be made permanent by setting keep_other_versions=true
|
||||
in the configuration file.
|
||||
|
||||
--only-deps Installs only the dependencies of the rock.
|
||||
]]..util.deps_mode_help()
|
||||
|
||||
|
||||
--- Install a binary rock.
|
||||
-- @param rock_file string: local or remote filename of a rock.
|
||||
-- @param deps_mode: string: Which trees to check dependencies for:
|
||||
-- "one" for the current default tree, "all" for all trees,
|
||||
-- "order" for all trees with priority >= the current default, "none" for no trees.
|
||||
-- @return (string, string) or (nil, string, [string]): Name and version of
|
||||
-- installed rock if succeeded or nil and an error message followed by an error code.
|
||||
function install.install_binary_rock(rock_file, deps_mode)
|
||||
assert(type(rock_file) == "string")
|
||||
|
||||
local name, version, arch = path.parse_name(rock_file)
|
||||
if not name then
|
||||
return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'."
|
||||
end
|
||||
|
||||
if arch ~= "all" and arch ~= cfg.arch then
|
||||
return nil, "Incompatible architecture "..arch, "arch"
|
||||
end
|
||||
if repos.is_installed(name, version) then
|
||||
repos.delete_version(name, version, deps_mode)
|
||||
end
|
||||
|
||||
local rollback = util.schedule_function(function()
|
||||
fs.delete(path.install_dir(name, version))
|
||||
fs.remove_dir_if_empty(path.versions_dir(name))
|
||||
end)
|
||||
|
||||
local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version))
|
||||
if not ok then return nil, err, errcode end
|
||||
|
||||
local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version))
|
||||
if err then
|
||||
return nil, "Failed loading rockspec for installed package: "..err, errcode
|
||||
end
|
||||
|
||||
if deps_mode == "none" then
|
||||
util.printerr("Warning: skipping dependency checks.")
|
||||
else
|
||||
ok, err, errcode = deps.check_external_deps(rockspec, "install")
|
||||
if err then return nil, err, errcode end
|
||||
end
|
||||
|
||||
-- For compatibility with .rock files built with LuaRocks 1
|
||||
if not fs.exists(path.rock_manifest_file(name, version)) then
|
||||
ok, err = manif.make_rock_manifest(name, version)
|
||||
if err then return nil, err end
|
||||
end
|
||||
|
||||
if deps_mode ~= "none" then
|
||||
ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode)
|
||||
if err then return nil, err, errcode end
|
||||
end
|
||||
|
||||
ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode)
|
||||
if err then return nil, err end
|
||||
|
||||
util.remove_scheduled_function(rollback)
|
||||
rollback = util.schedule_function(function()
|
||||
repos.delete_version(name, version, deps_mode)
|
||||
end)
|
||||
|
||||
ok, err = repos.run_hook(rockspec, "post_install")
|
||||
if err then return nil, err end
|
||||
|
||||
util.announce_install(rockspec)
|
||||
util.remove_scheduled_function(rollback)
|
||||
return name, version
|
||||
end
|
||||
|
||||
--- Installs the dependencies of a binary rock.
|
||||
-- @param rock_file string: local or remote filename of a rock.
|
||||
-- @param deps_mode: string: Which trees to check dependencies for:
|
||||
-- "one" for the current default tree, "all" for all trees,
|
||||
-- "order" for all trees with priority >= the current default, "none" for no trees.
|
||||
-- @return (string, string) or (nil, string, [string]): Name and version of
|
||||
-- the rock whose dependencies were installed if succeeded or nil and an error message
|
||||
-- followed by an error code.
|
||||
function install.install_binary_rock_deps(rock_file, deps_mode)
|
||||
assert(type(rock_file) == "string")
|
||||
|
||||
local name, version, arch = path.parse_name(rock_file)
|
||||
if not name then
|
||||
return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'."
|
||||
end
|
||||
|
||||
if arch ~= "all" and arch ~= cfg.arch then
|
||||
return nil, "Incompatible architecture "..arch, "arch"
|
||||
end
|
||||
|
||||
local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version))
|
||||
if not ok then return nil, err, errcode end
|
||||
|
||||
local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version))
|
||||
if err then
|
||||
return nil, "Failed loading rockspec for installed package: "..err, errcode
|
||||
end
|
||||
|
||||
ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode)
|
||||
if err then return nil, err, errcode end
|
||||
|
||||
util.printout()
|
||||
util.printout("Successfully installed dependencies for " ..name.." "..version)
|
||||
|
||||
return name, version
|
||||
end
|
||||
|
||||
--- Driver function for the "install" command.
|
||||
-- @param name string: name of a binary rock. If an URL or pathname
|
||||
-- to a binary rock is given, fetches and installs it. If a rockspec or a
|
||||
-- source rock is given, forwards the request to the "build" command.
|
||||
-- If a package name is given, forwards the request to "search" and,
|
||||
-- if returned a result, installs the matching rock.
|
||||
-- @param version string: When passing a package name, a version number
|
||||
-- may also be given.
|
||||
-- @return boolean or (nil, string, exitcode): True if installation was
|
||||
-- successful, nil and an error message otherwise. exitcode is optionally returned.
|
||||
function install.command(flags, name, version)
|
||||
if type(name) ~= "string" then
|
||||
return nil, "Argument missing. "..util.see_help("install")
|
||||
end
|
||||
|
||||
local ok, err = fs.check_command_permissions(flags)
|
||||
if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
|
||||
|
||||
if name:match("%.rockspec$") or name:match("%.src%.rock$") then
|
||||
local build = require("luarocks.build")
|
||||
return build.command(flags, name)
|
||||
elseif name:match("%.rock$") then
|
||||
if flags["only-deps"] then
|
||||
ok, err = install.install_binary_rock_deps(name, deps.get_deps_mode(flags))
|
||||
else
|
||||
ok, err = install.install_binary_rock(name, deps.get_deps_mode(flags))
|
||||
end
|
||||
if not ok then return nil, err end
|
||||
name, version = ok, err
|
||||
|
||||
if (not flags["only-deps"]) and (not flags["keep"]) and not cfg.keep_other_versions then
|
||||
local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"])
|
||||
if not ok then util.printerr(err) end
|
||||
end
|
||||
|
||||
manif.check_dependencies(nil, deps.get_deps_mode(flags))
|
||||
return name, version
|
||||
else
|
||||
local search = require("luarocks.search")
|
||||
local url, err = search.find_suitable_rock(search.make_query(name:lower(), version))
|
||||
if not url then
|
||||
return nil, err
|
||||
end
|
||||
util.printout("Installing "..url)
|
||||
return install.command(flags, url)
|
||||
end
|
||||
end
|
||||
|
||||
return install
|
||||
55
Utils/luarocks/lua/luarocks/lint.lua
Normal file
55
Utils/luarocks/lua/luarocks/lint.lua
Normal file
@ -0,0 +1,55 @@
|
||||
|
||||
--- Module implementing the LuaRocks "lint" command.
|
||||
-- Utility function that checks syntax of the rockspec.
|
||||
local lint = {}
|
||||
package.loaded["luarocks.lint"] = lint
|
||||
|
||||
local util = require("luarocks.util")
|
||||
local download = require("luarocks.download")
|
||||
local fetch = require("luarocks.fetch")
|
||||
|
||||
util.add_run_function(lint)
|
||||
lint.help_summary = "Check syntax of a rockspec."
|
||||
lint.help_arguments = "<rockspec>"
|
||||
lint.help = [[
|
||||
This is a utility function that checks the syntax of a rockspec.
|
||||
|
||||
It returns success or failure if the text of a rockspec is
|
||||
syntactically correct.
|
||||
]]
|
||||
|
||||
function lint.command(flags, input)
|
||||
if not input then
|
||||
return nil, "Argument missing. "..util.see_help("lint")
|
||||
end
|
||||
|
||||
local filename = input
|
||||
if not input:match(".rockspec$") then
|
||||
local err
|
||||
filename, err = download.download("rockspec", input:lower())
|
||||
if not filename then
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
|
||||
local rs, err = fetch.load_local_rockspec(filename)
|
||||
if not rs then
|
||||
return nil, "Failed loading rockspec: "..err
|
||||
end
|
||||
|
||||
local ok = true
|
||||
|
||||
-- This should have been done in the type checker,
|
||||
-- but it would break compatibility of other commands.
|
||||
-- Making 'lint' alone be stricter shouldn't be a problem,
|
||||
-- because extra-strict checks is what lint-type commands
|
||||
-- are all about.
|
||||
if not rs.description.license then
|
||||
util.printerr("Rockspec has no license field.")
|
||||
ok = false
|
||||
end
|
||||
|
||||
return ok, ok or filename.." failed consistency checks."
|
||||
end
|
||||
|
||||
return lint
|
||||
97
Utils/luarocks/lua/luarocks/list.lua
Normal file
97
Utils/luarocks/lua/luarocks/list.lua
Normal file
@ -0,0 +1,97 @@
|
||||
|
||||
--- Module implementing the LuaRocks "list" command.
|
||||
-- Lists currently installed rocks.
|
||||
local list = {}
|
||||
package.loaded["luarocks.list"] = list
|
||||
|
||||
local search = require("luarocks.search")
|
||||
local deps = require("luarocks.deps")
|
||||
local cfg = require("luarocks.cfg")
|
||||
local util = require("luarocks.util")
|
||||
local path = require("luarocks.path")
|
||||
|
||||
util.add_run_function(list)
|
||||
list.help_summary = "List currently installed rocks."
|
||||
list.help_arguments = "[--porcelain] <filter>"
|
||||
list.help = [[
|
||||
<filter> is a substring of a rock name to filter by.
|
||||
|
||||
--outdated List only rocks for which there is a
|
||||
higher version available in the rocks server.
|
||||
|
||||
--porcelain Produce machine-friendly output.
|
||||
]]
|
||||
|
||||
local function check_outdated(trees, query)
|
||||
local results_installed = {}
|
||||
for _, tree in ipairs(trees) do
|
||||
search.manifest_search(results_installed, path.rocks_dir(tree), query)
|
||||
end
|
||||
local outdated = {}
|
||||
for name, versions in util.sortedpairs(results_installed) do
|
||||
versions = util.keys(versions)
|
||||
table.sort(versions, deps.compare_versions)
|
||||
local latest_installed = versions[1]
|
||||
|
||||
local query_available = search.make_query(name:lower())
|
||||
query.exact_name = true
|
||||
local results_available, err = search.search_repos(query_available)
|
||||
|
||||
if results_available[name] then
|
||||
local available_versions = util.keys(results_available[name])
|
||||
table.sort(available_versions, deps.compare_versions)
|
||||
local latest_available = available_versions[1]
|
||||
local latest_available_repo = results_available[name][latest_available][1].repo
|
||||
|
||||
if deps.compare_versions(latest_available, latest_installed) then
|
||||
table.insert(outdated, { name = name, installed = latest_installed, available = latest_available, repo = latest_available_repo })
|
||||
end
|
||||
end
|
||||
end
|
||||
return outdated
|
||||
end
|
||||
|
||||
local function list_outdated(trees, query, porcelain)
|
||||
util.title("Outdated rocks:", porcelain)
|
||||
local outdated = check_outdated(trees, query)
|
||||
for _, item in ipairs(outdated) do
|
||||
if porcelain then
|
||||
util.printout(item.name, item.installed, item.available, item.repo)
|
||||
else
|
||||
util.printout(item.name)
|
||||
util.printout(" "..item.installed.." < "..item.available.." at "..item.repo)
|
||||
util.printout()
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Driver function for "list" command.
|
||||
-- @param filter string or nil: A substring of a rock name to filter by.
|
||||
-- @param version string or nil: a version may also be passed.
|
||||
-- @return boolean: True if succeeded, nil on errors.
|
||||
function list.command(flags, filter, version)
|
||||
local query = search.make_query(filter and filter:lower() or "", version)
|
||||
query.exact_name = false
|
||||
local trees = cfg.rocks_trees
|
||||
if flags["tree"] then
|
||||
trees = { flags["tree"] }
|
||||
end
|
||||
|
||||
if flags["outdated"] then
|
||||
return list_outdated(trees, query, flags["porcelain"])
|
||||
end
|
||||
|
||||
local results = {}
|
||||
for _, tree in ipairs(trees) do
|
||||
local ok, err, errcode = search.manifest_search(results, path.rocks_dir(tree), query)
|
||||
if not ok and errcode ~= "open" then
|
||||
util.warning(err)
|
||||
end
|
||||
end
|
||||
util.title("Installed rocks:", flags["porcelain"])
|
||||
search.print_results(results, flags["porcelain"])
|
||||
return true
|
||||
end
|
||||
|
||||
return list
|
||||
249
Utils/luarocks/lua/luarocks/loader.lua
Normal file
249
Utils/luarocks/lua/luarocks/loader.lua
Normal file
@ -0,0 +1,249 @@
|
||||
|
||||
--- A module which installs a Lua package loader that is LuaRocks-aware.
|
||||
-- This loader uses dependency information from the LuaRocks tree to load
|
||||
-- correct versions of modules. It does this by constructing a "context"
|
||||
-- table in the environment, which records which versions of packages were
|
||||
-- used to load previous modules, so that the loader chooses versions
|
||||
-- that are declared to be compatible with the ones loaded earlier.
|
||||
local loaders = package.loaders or package.searchers
|
||||
local package, require, ipairs, table, type, next, tostring, error =
|
||||
package, require, ipairs, table, type, next, tostring, error
|
||||
local unpack = unpack or table.unpack
|
||||
|
||||
--module("luarocks.loader")
|
||||
local loader = {}
|
||||
package.loaded["luarocks.loader"] = loader
|
||||
|
||||
local cfg = require("luarocks.cfg")
|
||||
cfg.init_package_paths()
|
||||
|
||||
local path = require("luarocks.path")
|
||||
local manif_core = require("luarocks.manif_core")
|
||||
local deps = require("luarocks.deps")
|
||||
local util = require("luarocks.util")
|
||||
|
||||
-- Workaround for wrappers produced by older versions of LuaRocks
|
||||
local temporary_global = false
|
||||
if luarocks then
|
||||
-- The site_config.lua file generated by old versions uses module(),
|
||||
-- so it produces a global `luarocks` table. Since we have the table,
|
||||
-- add the `loader` field to make the old wrappers happy.
|
||||
luarocks.loader = loader
|
||||
else
|
||||
-- When a new version is installed on top of an old version,
|
||||
-- site_config.lua may be replaced, and then it no longer creates
|
||||
-- a global.
|
||||
-- Detect when being called via -lluarocks.loader; this is
|
||||
-- most likely a wrapper.
|
||||
local info = debug.getinfo(2, "nS")
|
||||
if info.what == "C" and not info.name then
|
||||
luarocks = { loader = loader }
|
||||
temporary_global = true
|
||||
-- For the other half of this hack,
|
||||
-- see the next use of `temporary_global` below.
|
||||
end
|
||||
end
|
||||
|
||||
loader.context = {}
|
||||
|
||||
-- Contains a table when rocks trees are loaded,
|
||||
-- or 'false' to indicate rocks trees failed to load.
|
||||
-- 'nil' indicates rocks trees were not attempted to be loaded yet.
|
||||
loader.rocks_trees = nil
|
||||
|
||||
local function load_rocks_trees()
|
||||
local any_ok = false
|
||||
local trees = {}
|
||||
for _, tree in ipairs(cfg.rocks_trees) do
|
||||
local manifest, err = manif_core.load_local_manifest(path.rocks_dir(tree))
|
||||
if manifest then
|
||||
any_ok = true
|
||||
table.insert(trees, {tree=tree, manifest=manifest})
|
||||
end
|
||||
end
|
||||
if not any_ok then
|
||||
loader.rocks_trees = false
|
||||
return false
|
||||
end
|
||||
loader.rocks_trees = trees
|
||||
return true
|
||||
end
|
||||
|
||||
--- Process the dependencies of a package to determine its dependency
|
||||
-- chain for loading modules.
|
||||
-- @param name string: The name of an installed rock.
|
||||
-- @param version string: The version of the rock, in string format
|
||||
function loader.add_context(name, version)
|
||||
-- assert(type(name) == "string")
|
||||
-- assert(type(version) == "string")
|
||||
|
||||
if temporary_global then
|
||||
-- The first thing a wrapper does is to call add_context.
|
||||
-- From here on, it's safe to clean the global environment.
|
||||
luarocks = nil
|
||||
temporary_global = false
|
||||
end
|
||||
|
||||
if loader.context[name] then
|
||||
return
|
||||
end
|
||||
loader.context[name] = version
|
||||
|
||||
if not loader.rocks_trees and not load_rocks_trees() then
|
||||
return nil
|
||||
end
|
||||
|
||||
for _, tree in ipairs(loader.rocks_trees) do
|
||||
local manifest = tree.manifest
|
||||
|
||||
local pkgdeps
|
||||
if manifest.dependencies and manifest.dependencies[name] then
|
||||
pkgdeps = manifest.dependencies[name][version]
|
||||
end
|
||||
if not pkgdeps then
|
||||
return nil
|
||||
end
|
||||
for _, dep in ipairs(pkgdeps) do
|
||||
local pkg, constraints = dep.name, dep.constraints
|
||||
|
||||
for _, tree in ipairs(loader.rocks_trees) do
|
||||
local entries = tree.manifest.repository[pkg]
|
||||
if entries then
|
||||
for version, pkgs in util.sortedpairs(entries, deps.compare_versions) do
|
||||
if (not constraints) or deps.match_constraints(deps.parse_version(version), constraints) then
|
||||
loader.add_context(pkg, version)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Internal sorting function.
|
||||
-- @param a table: A provider table.
|
||||
-- @param b table: Another provider table.
|
||||
-- @return boolean: True if the version of a is greater than that of b.
|
||||
local function sort_versions(a,b)
|
||||
return a.version > b.version
|
||||
end
|
||||
|
||||
--- Request module to be loaded through other loaders,
|
||||
-- once the proper name of the module has been determined.
|
||||
-- For example, in case the module "socket.core" has been requested
|
||||
-- to the LuaRocks loader and it determined based on context that
|
||||
-- the version 2.0.2 needs to be loaded and it is not the current
|
||||
-- version, the module requested for the other loaders will be
|
||||
-- "socket.core_2_0_2".
|
||||
-- @param module The module name requested by the user, such as "socket.core"
|
||||
-- @param name The rock name, such as "luasocket"
|
||||
-- @param version The rock version, such as "2.0.2-1"
|
||||
-- @param module_name The actual module name, such as "socket.core" or "socket.core_2_0_2".
|
||||
-- @return table or (nil, string): The module table as returned by some other loader,
|
||||
-- or nil followed by an error message if no other loader managed to load the module.
|
||||
local function call_other_loaders(module, name, version, module_name)
|
||||
for i, a_loader in ipairs(loaders) do
|
||||
if a_loader ~= loader.luarocks_loader then
|
||||
local results = { a_loader(module_name) }
|
||||
if type(results[1]) == "function" then
|
||||
return unpack(results)
|
||||
end
|
||||
end
|
||||
end
|
||||
return "Failed loading module "..module.." in LuaRocks rock "..name.." "..version
|
||||
end
|
||||
|
||||
--- Search for a module in the rocks trees
|
||||
-- @param module string: module name (eg. "socket.core")
|
||||
-- @param filter_file_name function(string, string, string, string, number):
|
||||
-- a function that takes the module file name (eg "socket/core.so"), the rock name
|
||||
-- (eg "luasocket"), the version (eg "2.0.2-1"), the path of the rocks tree
|
||||
-- (eg "/usr/local"), and the numeric index of the matching entry, so the
|
||||
-- filter function can know if the matching module was the first entry or not.
|
||||
-- @return string, string, string, (string or table):
|
||||
-- * name of the rock containing the module (eg. "luasocket")
|
||||
-- * version of the rock (eg. "2.0.2-1")
|
||||
-- * return value of filter_file_name
|
||||
-- * tree of the module (string or table in `rocks_trees` format)
|
||||
local function select_module(module, filter_file_name)
|
||||
--assert(type(module) == "string")
|
||||
--assert(type(filter_module_name) == "function")
|
||||
|
||||
if not loader.rocks_trees and not load_rocks_trees() then
|
||||
return nil
|
||||
end
|
||||
|
||||
local providers = {}
|
||||
for _, tree in ipairs(loader.rocks_trees) do
|
||||
local entries = tree.manifest.modules[module]
|
||||
if entries then
|
||||
for i, entry in ipairs(entries) do
|
||||
local name, version = entry:match("^([^/]*)/(.*)$")
|
||||
local file_name = tree.manifest.repository[name][version][1].modules[module]
|
||||
if type(file_name) ~= "string" then
|
||||
error("Invalid data in manifest file for module "..tostring(module).." (invalid data for "..tostring(name).." "..tostring(version)..")")
|
||||
end
|
||||
file_name = filter_file_name(file_name, name, version, tree.tree, i)
|
||||
if loader.context[name] == version then
|
||||
return name, version, file_name
|
||||
end
|
||||
version = deps.parse_version(version)
|
||||
table.insert(providers, {name = name, version = version, module_name = file_name, tree = tree})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if next(providers) then
|
||||
table.sort(providers, sort_versions)
|
||||
local first = providers[1]
|
||||
return first.name, first.version.string, first.module_name, first.tree
|
||||
end
|
||||
end
|
||||
|
||||
--- Search for a module
|
||||
-- @param module string: module name (eg. "socket.core")
|
||||
-- @return string, string, string, (string or table):
|
||||
-- * name of the rock containing the module (eg. "luasocket")
|
||||
-- * version of the rock (eg. "2.0.2-1")
|
||||
-- * name of the module (eg. "socket.core", or "socket.core_2_0_2" if file is stored versioned).
|
||||
-- * tree of the module (string or table in `rocks_trees` format)
|
||||
local function pick_module(module)
|
||||
return
|
||||
select_module(module, function(file_name, name, version, tree, i)
|
||||
if i > 1 then
|
||||
file_name = path.versioned_name(file_name, "", name, version)
|
||||
end
|
||||
return path.path_to_module(file_name)
|
||||
end)
|
||||
end
|
||||
|
||||
--- Return the pathname of the file that would be loaded for a module.
|
||||
-- @param module string: module name (eg. "socket.core")
|
||||
-- @return string: filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so")
|
||||
function loader.which(module)
|
||||
local _, _, file_name = select_module(module, path.which_i)
|
||||
return file_name
|
||||
end
|
||||
|
||||
--- Package loader for LuaRocks support.
|
||||
-- A module is searched in installed rocks that match the
|
||||
-- current LuaRocks context. If module is not part of the
|
||||
-- context, or if a context has not yet been set, the module
|
||||
-- in the package with the highest version is used.
|
||||
-- @param module string: The module name, like in plain require().
|
||||
-- @return table: The module table (typically), like in plain
|
||||
-- require(). See <a href="http://www.lua.org/manual/5.1/manual.html#pdf-require">require()</a>
|
||||
-- in the Lua reference manual for details.
|
||||
function loader.luarocks_loader(module)
|
||||
local name, version, module_name = pick_module(module)
|
||||
if not name then
|
||||
return "No LuaRocks module found for "..module
|
||||
else
|
||||
loader.add_context(name, version)
|
||||
return call_other_loaders(module, name, version, module_name)
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(loaders, 1, loader.luarocks_loader)
|
||||
|
||||
return loader
|
||||
92
Utils/luarocks/lua/luarocks/make.lua
Normal file
92
Utils/luarocks/lua/luarocks/make.lua
Normal file
@ -0,0 +1,92 @@
|
||||
|
||||
--- Module implementing the LuaRocks "make" command.
|
||||
-- Builds sources in the current directory, but unlike "build",
|
||||
-- it does not fetch sources, etc., assuming everything is
|
||||
-- available in the current directory.
|
||||
local make = {}
|
||||
package.loaded["luarocks.make"] = make
|
||||
|
||||
local build = require("luarocks.build")
|
||||
local fs = require("luarocks.fs")
|
||||
local util = require("luarocks.util")
|
||||
local cfg = require("luarocks.cfg")
|
||||
local fetch = require("luarocks.fetch")
|
||||
local pack = require("luarocks.pack")
|
||||
local remove = require("luarocks.remove")
|
||||
local deps = require("luarocks.deps")
|
||||
local manif = require("luarocks.manif")
|
||||
|
||||
util.add_run_function(make)
|
||||
make.help_summary = "Compile package in current directory using a rockspec."
|
||||
make.help_arguments = "[--pack-binary-rock] [<rockspec>]"
|
||||
make.help = [[
|
||||
Builds sources in the current directory, but unlike "build",
|
||||
it does not fetch sources, etc., assuming everything is
|
||||
available in the current directory. If no argument is given,
|
||||
it looks for a rockspec in the current directory and in "rockspec/"
|
||||
and "rockspecs/" subdirectories, picking the rockspec with newest version
|
||||
or without version name. If rockspecs for different rocks are found
|
||||
or there are several rockspecs without version, you must specify which to use,
|
||||
through the command-line.
|
||||
|
||||
This command is useful as a tool for debugging rockspecs.
|
||||
To install rocks, you'll normally want to use the "install" and
|
||||
"build" commands. See the help on those for details.
|
||||
|
||||
--pack-binary-rock Do not install rock. Instead, produce a .rock file
|
||||
with the contents of compilation in the current
|
||||
directory.
|
||||
|
||||
--keep Do not remove previously installed versions of the
|
||||
rock after installing a new one. This behavior can
|
||||
be made permanent by setting keep_other_versions=true
|
||||
in the configuration file.
|
||||
|
||||
--branch=<name> Override the `source.branch` field in the loaded
|
||||
rockspec. Allows to specify a different branch to
|
||||
fetch. Particularly for SCM rocks.
|
||||
|
||||
]]
|
||||
|
||||
--- Driver function for "make" command.
|
||||
-- @param name string: A local rockspec.
|
||||
-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an
|
||||
-- error message otherwise. exitcode is optionally returned.
|
||||
function make.command(flags, rockspec)
|
||||
assert(type(rockspec) == "string" or not rockspec)
|
||||
|
||||
if not rockspec then
|
||||
local err
|
||||
rockspec, err = util.get_default_rockspec()
|
||||
if not rockspec then
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
if not rockspec:match("rockspec$") then
|
||||
return nil, "Invalid argument: 'make' takes a rockspec as a parameter. "..util.see_help("make")
|
||||
end
|
||||
|
||||
if flags["pack-binary-rock"] then
|
||||
local rspec, err, errcode = fetch.load_rockspec(rockspec)
|
||||
if not rspec then
|
||||
return nil, err
|
||||
end
|
||||
return pack.pack_binary_rock(rspec.name, rspec.version, build.build_rockspec, rockspec, false, true, deps.get_deps_mode(flags))
|
||||
else
|
||||
local ok, err = fs.check_command_permissions(flags)
|
||||
if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
|
||||
ok, err = build.build_rockspec(rockspec, false, true, deps.get_deps_mode(flags))
|
||||
if not ok then return nil, err end
|
||||
local name, version = ok, err
|
||||
|
||||
if (not flags["keep"]) and not cfg.keep_other_versions then
|
||||
local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"])
|
||||
if not ok then util.printerr(err) end
|
||||
end
|
||||
|
||||
manif.check_dependencies(nil, deps.get_deps_mode(flags))
|
||||
return name, version
|
||||
end
|
||||
end
|
||||
|
||||
return make
|
||||
53
Utils/luarocks/lua/luarocks/make_manifest.lua
Normal file
53
Utils/luarocks/lua/luarocks/make_manifest.lua
Normal file
@ -0,0 +1,53 @@
|
||||
|
||||
--- Module implementing the luarocks-admin "make_manifest" command.
|
||||
-- Compile a manifest file for a repository.
|
||||
local make_manifest = {}
|
||||
package.loaded["luarocks.make_manifest"] = make_manifest
|
||||
|
||||
local manif = require("luarocks.manif")
|
||||
local index = require("luarocks.index")
|
||||
local cfg = require("luarocks.cfg")
|
||||
local util = require("luarocks.util")
|
||||
local deps = require("luarocks.deps")
|
||||
local fs = require("luarocks.fs")
|
||||
local dir = require("luarocks.dir")
|
||||
|
||||
util.add_run_function(make_manifest)
|
||||
make_manifest.help_summary = "Compile a manifest file for a repository."
|
||||
|
||||
make_manifest.help = [[
|
||||
<argument>, if given, is a local repository pathname.
|
||||
|
||||
--local-tree If given, do not write versioned versions of the manifest file.
|
||||
Use this when rebuilding the manifest of a local rocks tree.
|
||||
]]
|
||||
|
||||
--- Driver function for "make_manifest" command.
|
||||
-- @param repo string or nil: Pathname of a local repository. If not given,
|
||||
-- the default local repository configured as cfg.rocks_dir is used.
|
||||
-- @return boolean or (nil, string): True if manifest was generated,
|
||||
-- or nil and an error message.
|
||||
function make_manifest.command(flags, repo)
|
||||
assert(type(repo) == "string" or not repo)
|
||||
repo = repo or cfg.rocks_dir
|
||||
|
||||
util.printout("Making manifest for "..repo)
|
||||
|
||||
if repo:match("/lib/luarocks") and not flags["local-tree"] then
|
||||
util.warning("This looks like a local rocks tree, but you did not pass --local-tree.")
|
||||
end
|
||||
|
||||
local ok, err = manif.make_manifest(repo, deps.get_deps_mode(flags), not flags["local-tree"])
|
||||
if ok and not flags["local-tree"] then
|
||||
util.printout("Generating index.html for "..repo)
|
||||
index.make_index(repo)
|
||||
end
|
||||
if flags["local-tree"] then
|
||||
for luaver in util.lua_versions() do
|
||||
fs.delete(dir.path(repo, "manifest-"..luaver))
|
||||
end
|
||||
end
|
||||
return ok, err
|
||||
end
|
||||
|
||||
return make_manifest
|
||||
628
Utils/luarocks/lua/luarocks/manif.lua
Normal file
628
Utils/luarocks/lua/luarocks/manif.lua
Normal file
@ -0,0 +1,628 @@
|
||||
--- Module for handling manifest files and tables.
|
||||
-- Manifest files describe the contents of a LuaRocks tree or server.
|
||||
-- They are loaded into manifest tables, which are then used for
|
||||
-- performing searches, matching dependencies, etc.
|
||||
local manif = {}
|
||||
package.loaded["luarocks.manif"] = manif
|
||||
|
||||
local manif_core = require("luarocks.manif_core")
|
||||
local persist = require("luarocks.persist")
|
||||
local fetch = require("luarocks.fetch")
|
||||
local dir = require("luarocks.dir")
|
||||
local fs = require("luarocks.fs")
|
||||
local search = require("luarocks.search")
|
||||
local util = require("luarocks.util")
|
||||
local cfg = require("luarocks.cfg")
|
||||
local path = require("luarocks.path")
|
||||
local repos = require("luarocks.repos")
|
||||
local deps = require("luarocks.deps")
|
||||
|
||||
manif.rock_manifest_cache = {}
|
||||
|
||||
--- Commit a table to disk in given local path.
|
||||
-- @param where string: The directory where the table should be saved.
|
||||
-- @param name string: The filename.
|
||||
-- @param tbl table: The table to be saved.
|
||||
-- @return boolean or (nil, string): true if successful, or nil and a
|
||||
-- message in case of errors.
|
||||
local function save_table(where, name, tbl)
|
||||
assert(type(where) == "string")
|
||||
assert(type(name) == "string")
|
||||
assert(type(tbl) == "table")
|
||||
|
||||
local filename = dir.path(where, name)
|
||||
local ok, err = persist.save_from_table(filename..".tmp", tbl)
|
||||
if ok then
|
||||
ok, err = fs.replace_file(filename, filename..".tmp")
|
||||
end
|
||||
return ok, err
|
||||
end
|
||||
|
||||
function manif.load_rock_manifest(name, version, root)
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
|
||||
local name_version = name.."/"..version
|
||||
if manif.rock_manifest_cache[name_version] then
|
||||
return manif.rock_manifest_cache[name_version].rock_manifest
|
||||
end
|
||||
local pathname = path.rock_manifest_file(name, version, root)
|
||||
local rock_manifest = persist.load_into_table(pathname)
|
||||
if not rock_manifest then return nil end
|
||||
manif.rock_manifest_cache[name_version] = rock_manifest
|
||||
return rock_manifest.rock_manifest
|
||||
end
|
||||
|
||||
function manif.make_rock_manifest(name, version)
|
||||
local install_dir = path.install_dir(name, version)
|
||||
local tree = {}
|
||||
for _, file in ipairs(fs.find(install_dir)) do
|
||||
local full_path = dir.path(install_dir, file)
|
||||
local walk = tree
|
||||
local last
|
||||
local last_name
|
||||
for name in file:gmatch("[^/]+") do
|
||||
local next = walk[name]
|
||||
if not next then
|
||||
next = {}
|
||||
walk[name] = next
|
||||
end
|
||||
last = walk
|
||||
last_name = name
|
||||
walk = next
|
||||
end
|
||||
if fs.is_file(full_path) then
|
||||
local sum, err = fs.get_md5(full_path)
|
||||
if not sum then
|
||||
return nil, "Failed producing checksum: "..tostring(err)
|
||||
end
|
||||
last[last_name] = sum
|
||||
end
|
||||
end
|
||||
local rock_manifest = { rock_manifest=tree }
|
||||
manif.rock_manifest_cache[name.."/"..version] = rock_manifest
|
||||
save_table(install_dir, "rock_manifest", rock_manifest )
|
||||
end
|
||||
|
||||
local function fetch_manifest_from(repo_url, filename)
|
||||
local url = dir.path(repo_url, filename)
|
||||
local name = repo_url:gsub("[/:]","_")
|
||||
local cache_dir = dir.path(cfg.local_cache, name)
|
||||
local ok = fs.make_dir(cache_dir)
|
||||
if not ok then
|
||||
return nil, "Failed creating temporary cache directory "..cache_dir
|
||||
end
|
||||
local file, err, errcode = fetch.fetch_url(url, dir.path(cache_dir, filename), true)
|
||||
if not file then
|
||||
return nil, "Failed fetching manifest for "..repo_url..(err and " - "..err or ""), errcode
|
||||
end
|
||||
return file
|
||||
end
|
||||
|
||||
--- Load a local or remote manifest describing a repository.
|
||||
-- All functions that use manifest tables assume they were obtained
|
||||
-- through either this function or load_local_manifest.
|
||||
-- @param repo_url string: URL or pathname for the repository.
|
||||
-- @param lua_version string: Lua version in "5.x" format, defaults to installed version.
|
||||
-- @return table or (nil, string, [string]): A table representing the manifest,
|
||||
-- or nil followed by an error message and an optional error code.
|
||||
function manif.load_manifest(repo_url, lua_version)
|
||||
assert(type(repo_url) == "string")
|
||||
assert(type(lua_version) == "string" or not lua_version)
|
||||
lua_version = lua_version or cfg.lua_version
|
||||
|
||||
local cached_manifest = manif_core.get_cached_manifest(repo_url, lua_version)
|
||||
if cached_manifest then
|
||||
return cached_manifest
|
||||
end
|
||||
|
||||
local filenames = {
|
||||
"manifest-"..lua_version..".zip",
|
||||
"manifest-"..lua_version,
|
||||
"manifest",
|
||||
}
|
||||
|
||||
local protocol, repodir = dir.split_url(repo_url)
|
||||
local pathname
|
||||
if protocol == "file" then
|
||||
for _, filename in ipairs(filenames) do
|
||||
pathname = dir.path(repodir, filename)
|
||||
if fs.exists(pathname) then
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
local err, errcode
|
||||
for _, filename in ipairs(filenames) do
|
||||
pathname, err, errcode = fetch_manifest_from(repo_url, filename)
|
||||
if pathname then
|
||||
break
|
||||
end
|
||||
end
|
||||
if not pathname then
|
||||
return nil, err, errcode
|
||||
end
|
||||
end
|
||||
if pathname:match(".*%.zip$") then
|
||||
pathname = fs.absolute_name(pathname)
|
||||
local dir = dir.dir_name(pathname)
|
||||
fs.change_dir(dir)
|
||||
local nozip = pathname:match("(.*)%.zip$")
|
||||
fs.delete(nozip)
|
||||
local ok = fs.unzip(pathname)
|
||||
fs.pop_dir()
|
||||
if not ok then
|
||||
fs.delete(pathname)
|
||||
fs.delete(pathname..".timestamp")
|
||||
return nil, "Failed extracting manifest file"
|
||||
end
|
||||
pathname = nozip
|
||||
end
|
||||
return manif_core.manifest_loader(pathname, repo_url, lua_version)
|
||||
end
|
||||
|
||||
--- Update storage table to account for items provided by a package.
|
||||
-- @param storage table: a table storing items in the following format:
|
||||
-- keys are item names and values are arrays of packages providing each item,
|
||||
-- where a package is specified as string `name/version`.
|
||||
-- @param items table: a table mapping item names to paths.
|
||||
-- @param name string: package name.
|
||||
-- @param version string: package version.
|
||||
local function store_package_items(storage, name, version, items)
|
||||
assert(type(storage) == "table")
|
||||
assert(type(items) == "table")
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
|
||||
local package_identifier = name.."/"..version
|
||||
|
||||
for item_name, path in pairs(items) do
|
||||
if not storage[item_name] then
|
||||
storage[item_name] = {}
|
||||
end
|
||||
|
||||
table.insert(storage[item_name], package_identifier)
|
||||
end
|
||||
end
|
||||
|
||||
--- Update storage table removing items provided by a package.
|
||||
-- @param storage table: a table storing items in the following format:
|
||||
-- keys are item names and values are arrays of packages providing each item,
|
||||
-- where a package is specified as string `name/version`.
|
||||
-- @param items table: a table mapping item names to paths.
|
||||
-- @param name string: package name.
|
||||
-- @param version string: package version.
|
||||
local function remove_package_items(storage, name, version, items)
|
||||
assert(type(storage) == "table")
|
||||
assert(type(items) == "table")
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
|
||||
local package_identifier = name.."/"..version
|
||||
|
||||
for item_name, path in pairs(items) do
|
||||
local all_identifiers = storage[item_name]
|
||||
|
||||
for i, identifier in ipairs(all_identifiers) do
|
||||
if identifier == package_identifier then
|
||||
table.remove(all_identifiers, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if #all_identifiers == 0 then
|
||||
storage[item_name] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Sort function for ordering rock identifiers in a manifest's
|
||||
-- modules table. Rocks are ordered alphabetically by name, and then
|
||||
-- by version which greater first.
|
||||
-- @param a string: Version to compare.
|
||||
-- @param b string: Version to compare.
|
||||
-- @return boolean: The comparison result, according to the
|
||||
-- rule outlined above.
|
||||
local function sort_pkgs(a, b)
|
||||
assert(type(a) == "string")
|
||||
assert(type(b) == "string")
|
||||
|
||||
local na, va = a:match("(.*)/(.*)$")
|
||||
local nb, vb = b:match("(.*)/(.*)$")
|
||||
|
||||
return (na == nb) and deps.compare_versions(va, vb) or na < nb
|
||||
end
|
||||
|
||||
--- Sort items of a package matching table by version number (higher versions first).
|
||||
-- @param tbl table: the package matching table: keys should be strings
|
||||
-- and values arrays of strings with packages names in "name/version" format.
|
||||
local function sort_package_matching_table(tbl)
|
||||
assert(type(tbl) == "table")
|
||||
|
||||
if next(tbl) then
|
||||
for item, pkgs in pairs(tbl) do
|
||||
if #pkgs > 1 then
|
||||
table.sort(pkgs, sort_pkgs)
|
||||
-- Remove duplicates from the sorted array.
|
||||
local prev = nil
|
||||
local i = 1
|
||||
while pkgs[i] do
|
||||
local curr = pkgs[i]
|
||||
if curr == prev then
|
||||
table.remove(pkgs, i)
|
||||
else
|
||||
prev = curr
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Process the dependencies of a manifest table to determine its dependency
|
||||
-- chains for loading modules. The manifest dependencies information is filled
|
||||
-- and any dependency inconsistencies or missing dependencies are reported to
|
||||
-- standard error.
|
||||
-- @param manifest table: a manifest table.
|
||||
-- @param deps_mode string: Dependency mode: "one" for the current default tree,
|
||||
-- "all" for all trees, "order" for all trees with priority >= the current default,
|
||||
-- "none" for no trees.
|
||||
local function update_dependencies(manifest, deps_mode)
|
||||
assert(type(manifest) == "table")
|
||||
assert(type(deps_mode) == "string")
|
||||
|
||||
for pkg, versions in pairs(manifest.repository) do
|
||||
for version, repositories in pairs(versions) do
|
||||
for _, repo in ipairs(repositories) do
|
||||
if repo.arch == "installed" then
|
||||
repo.dependencies = {}
|
||||
deps.scan_deps(repo.dependencies, manifest, pkg, version, deps_mode)
|
||||
repo.dependencies[pkg] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Filter manifest table by Lua version, removing rockspecs whose Lua version
|
||||
-- does not match.
|
||||
-- @param manifest table: a manifest table.
|
||||
-- @param lua_version string or nil: filter by Lua version
|
||||
-- @param repodir string: directory of repository being scanned
|
||||
-- @param cache table: temporary rockspec cache table
|
||||
local function filter_by_lua_version(manifest, lua_version, repodir, cache)
|
||||
assert(type(manifest) == "table")
|
||||
assert(type(repodir) == "string")
|
||||
assert((not cache) or type(cache) == "table")
|
||||
|
||||
cache = cache or {}
|
||||
lua_version = deps.parse_version(lua_version)
|
||||
for pkg, versions in pairs(manifest.repository) do
|
||||
local to_remove = {}
|
||||
for version, repositories in pairs(versions) do
|
||||
for _, repo in ipairs(repositories) do
|
||||
if repo.arch == "rockspec" then
|
||||
local pathname = dir.path(repodir, pkg.."-"..version..".rockspec")
|
||||
local rockspec, err = cache[pathname]
|
||||
if not rockspec then
|
||||
rockspec, err = fetch.load_local_rockspec(pathname, true)
|
||||
end
|
||||
if rockspec then
|
||||
cache[pathname] = rockspec
|
||||
for _, dep in ipairs(rockspec.dependencies) do
|
||||
if dep.name == "lua" then
|
||||
if not deps.match_constraints(lua_version, dep.constraints) then
|
||||
table.insert(to_remove, version)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
util.printerr("Error loading rockspec for "..pkg.." "..version..": "..err)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if next(to_remove) then
|
||||
for _, incompat in ipairs(to_remove) do
|
||||
versions[incompat] = nil
|
||||
end
|
||||
if not next(versions) then
|
||||
manifest.repository[pkg] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Store search results in a manifest table.
|
||||
-- @param results table: The search results as returned by search.disk_search.
|
||||
-- @param manifest table: A manifest table (must contain repository, modules, commands tables).
|
||||
-- It will be altered to include the search results.
|
||||
-- @return boolean or (nil, string): true in case of success, or nil followed by an error message.
|
||||
local function store_results(results, manifest)
|
||||
assert(type(results) == "table")
|
||||
assert(type(manifest) == "table")
|
||||
|
||||
for name, versions in pairs(results) do
|
||||
local pkgtable = manifest.repository[name] or {}
|
||||
for version, entries in pairs(versions) do
|
||||
local versiontable = {}
|
||||
for _, entry in ipairs(entries) do
|
||||
local entrytable = {}
|
||||
entrytable.arch = entry.arch
|
||||
if entry.arch == "installed" then
|
||||
local rock_manifest = manif.load_rock_manifest(name, version)
|
||||
if not rock_manifest then
|
||||
return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?"
|
||||
end
|
||||
|
||||
entrytable.modules = repos.package_modules(name, version)
|
||||
store_package_items(manifest.modules, name, version, entrytable.modules)
|
||||
entrytable.commands = repos.package_commands(name, version)
|
||||
store_package_items(manifest.commands, name, version, entrytable.commands)
|
||||
end
|
||||
table.insert(versiontable, entrytable)
|
||||
end
|
||||
pkgtable[version] = versiontable
|
||||
end
|
||||
manifest.repository[name] = pkgtable
|
||||
end
|
||||
sort_package_matching_table(manifest.modules)
|
||||
sort_package_matching_table(manifest.commands)
|
||||
return true
|
||||
end
|
||||
|
||||
--- Scan a LuaRocks repository and output a manifest file.
|
||||
-- A file called 'manifest' will be written in the root of the given
|
||||
-- repository directory.
|
||||
-- @param repo A local repository directory.
|
||||
-- @param deps_mode string: Dependency mode: "one" for the current default tree,
|
||||
-- "all" for all trees, "order" for all trees with priority >= the current default,
|
||||
-- "none" for the default dependency mode from the configuration.
|
||||
-- @param remote boolean: 'true' if making a manifest for a rocks server.
|
||||
-- @return boolean or (nil, string): True if manifest was generated,
|
||||
-- or nil and an error message.
|
||||
function manif.make_manifest(repo, deps_mode, remote)
|
||||
assert(type(repo) == "string")
|
||||
assert(type(deps_mode) == "string")
|
||||
|
||||
if deps_mode == "none" then deps_mode = cfg.deps_mode end
|
||||
|
||||
if not fs.is_dir(repo) then
|
||||
return nil, "Cannot access repository at "..repo
|
||||
end
|
||||
|
||||
local query = search.make_query("")
|
||||
query.exact_name = false
|
||||
query.arch = "any"
|
||||
local results = search.disk_search(repo, query)
|
||||
local manifest = { repository = {}, modules = {}, commands = {} }
|
||||
|
||||
manif_core.cache_manifest(repo, nil, manifest)
|
||||
|
||||
local ok, err = store_results(results, manifest)
|
||||
if not ok then return nil, err end
|
||||
|
||||
if remote then
|
||||
local cache = {}
|
||||
for luaver in util.lua_versions() do
|
||||
local vmanifest = { repository = {}, modules = {}, commands = {} }
|
||||
local ok, err = store_results(results, vmanifest)
|
||||
filter_by_lua_version(vmanifest, luaver, repo, cache)
|
||||
save_table(repo, "manifest-"..luaver, vmanifest)
|
||||
end
|
||||
else
|
||||
update_dependencies(manifest, deps_mode)
|
||||
end
|
||||
|
||||
return save_table(repo, "manifest", manifest)
|
||||
end
|
||||
|
||||
--- Update manifest file for a local repository
|
||||
-- adding information about a version of a package installed in that repository.
|
||||
-- @param name string: Name of a package from the repository.
|
||||
-- @param version string: Version of a package from the repository.
|
||||
-- @param repo string or nil: Pathname of a local repository. If not given,
|
||||
-- the default local repository is used.
|
||||
-- @param deps_mode string: Dependency mode: "one" for the current default tree,
|
||||
-- "all" for all trees, "order" for all trees with priority >= the current default,
|
||||
-- "none" for using the default dependency mode from the configuration.
|
||||
-- @return boolean or (nil, string): True if manifest was updated successfully,
|
||||
-- or nil and an error message.
|
||||
function manif.add_to_manifest(name, version, repo, deps_mode)
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
|
||||
assert(type(deps_mode) == "string")
|
||||
|
||||
if deps_mode == "none" then deps_mode = cfg.deps_mode end
|
||||
|
||||
local manifest, err = manif_core.load_local_manifest(rocks_dir)
|
||||
if not manifest then
|
||||
util.printerr("No existing manifest. Attempting to rebuild...")
|
||||
-- Manifest built by `manif.make_manifest` should already
|
||||
-- include information about given name and version,
|
||||
-- no need to update it.
|
||||
return manif.make_manifest(rocks_dir, deps_mode)
|
||||
end
|
||||
|
||||
local results = {[name] = {[version] = {{arch = "installed", repo = rocks_dir}}}}
|
||||
|
||||
local ok, err = store_results(results, manifest)
|
||||
if not ok then return nil, err end
|
||||
|
||||
update_dependencies(manifest, deps_mode)
|
||||
return save_table(rocks_dir, "manifest", manifest)
|
||||
end
|
||||
|
||||
--- Update manifest file for a local repository
|
||||
-- removing information about a version of a package.
|
||||
-- @param name string: Name of a package removed from the repository.
|
||||
-- @param version string: Version of a package removed from the repository.
|
||||
-- @param repo string or nil: Pathname of a local repository. If not given,
|
||||
-- the default local repository is used.
|
||||
-- @param deps_mode string: Dependency mode: "one" for the current default tree,
|
||||
-- "all" for all trees, "order" for all trees with priority >= the current default,
|
||||
-- "none" for using the default dependency mode from the configuration.
|
||||
-- @return boolean or (nil, string): True if manifest was updated successfully,
|
||||
-- or nil and an error message.
|
||||
function manif.remove_from_manifest(name, version, repo, deps_mode)
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
|
||||
assert(type(deps_mode) == "string")
|
||||
|
||||
if deps_mode == "none" then deps_mode = cfg.deps_mode end
|
||||
|
||||
local manifest, err = manif_core.load_local_manifest(rocks_dir)
|
||||
if not manifest then
|
||||
util.printerr("No existing manifest. Attempting to rebuild...")
|
||||
-- Manifest built by `manif.make_manifest` should already
|
||||
-- include up-to-date information, no need to update it.
|
||||
return manif.make_manifest(rocks_dir, deps_mode)
|
||||
end
|
||||
|
||||
local package_entry = manifest.repository[name]
|
||||
|
||||
local version_entry = package_entry[version][1]
|
||||
remove_package_items(manifest.modules, name, version, version_entry.modules)
|
||||
remove_package_items(manifest.commands, name, version, version_entry.commands)
|
||||
|
||||
package_entry[version] = nil
|
||||
manifest.dependencies[name][version] = nil
|
||||
|
||||
if not next(package_entry) then
|
||||
-- No more versions of this package.
|
||||
manifest.repository[name] = nil
|
||||
manifest.dependencies[name] = nil
|
||||
end
|
||||
|
||||
update_dependencies(manifest, deps_mode)
|
||||
return save_table(rocks_dir, "manifest", manifest)
|
||||
end
|
||||
|
||||
--- Report missing dependencies for all rocks installed in a repository.
|
||||
-- @param repo string or nil: Pathname of a local repository. If not given,
|
||||
-- the default local repository is used.
|
||||
-- @param deps_mode string: Dependency mode: "one" for the current default tree,
|
||||
-- "all" for all trees, "order" for all trees with priority >= the current default,
|
||||
-- "none" for using the default dependency mode from the configuration.
|
||||
function manif.check_dependencies(repo, deps_mode)
|
||||
local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
|
||||
assert(type(deps_mode) == "string")
|
||||
if deps_mode == "none" then deps_mode = cfg.deps_mode end
|
||||
|
||||
local manifest = manif_core.load_local_manifest(rocks_dir)
|
||||
if not manifest then
|
||||
return
|
||||
end
|
||||
|
||||
for name, versions in util.sortedpairs(manifest.repository) do
|
||||
for version, version_entries in util.sortedpairs(versions, deps.compare_versions) do
|
||||
for _, entry in ipairs(version_entries) do
|
||||
if entry.arch == "installed" then
|
||||
if manifest.dependencies[name] and manifest.dependencies[name][version] then
|
||||
deps.report_missing_dependencies(name, version, manifest.dependencies[name][version], deps_mode)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function manif.zip_manifests()
|
||||
for ver in util.lua_versions() do
|
||||
local file = "manifest-"..ver
|
||||
local zip = file..".zip"
|
||||
fs.delete(dir.path(fs.current_dir(), zip))
|
||||
fs.zip(zip, file)
|
||||
end
|
||||
end
|
||||
|
||||
--- Get type and name of an item (a module or a command) provided by a file.
|
||||
-- @param deploy_type string: rock manifest subtree the file comes from ("bin", "lua", or "lib").
|
||||
-- @param file_path string: path to the file relatively to deploy_type subdirectory.
|
||||
-- @return (string, string): item type ("module" or "command") and name.
|
||||
function manif.get_provided_item(deploy_type, file_path)
|
||||
assert(type(deploy_type) == "string")
|
||||
assert(type(file_path) == "string")
|
||||
local item_type = deploy_type == "bin" and "command" or "module"
|
||||
local item_name = item_type == "command" and file_path or path.path_to_module(file_path)
|
||||
return item_type, item_name
|
||||
end
|
||||
|
||||
local function get_providers(item_type, item_name, repo)
|
||||
assert(type(item_type) == "string")
|
||||
assert(type(item_name) == "string")
|
||||
local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
|
||||
local manifest = manif_core.load_local_manifest(rocks_dir)
|
||||
return manifest and manifest[item_type .. "s"][item_name]
|
||||
end
|
||||
|
||||
--- Given a name of a module or a command, figure out which rock name and version
|
||||
-- correspond to it in the rock tree manifest.
|
||||
-- @param item_type string: "module" or "command".
|
||||
-- @param item_name string: module or command name.
|
||||
-- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used.
|
||||
-- @return (string, string) or nil: name and version of the provider rock or nil if there
|
||||
-- is no provider.
|
||||
function manif.get_current_provider(item_type, item_name, repo)
|
||||
local providers = get_providers(item_type, item_name, repo)
|
||||
if providers then
|
||||
return providers[1]:match("([^/]*)/([^/]*)")
|
||||
end
|
||||
end
|
||||
|
||||
function manif.get_next_provider(item_type, item_name, repo)
|
||||
local providers = get_providers(item_type, item_name, repo)
|
||||
if providers and providers[2] then
|
||||
return providers[2]:match("([^/]*)/([^/]*)")
|
||||
end
|
||||
end
|
||||
|
||||
--- Given a name of a module or a command provided by a package, figure out
|
||||
-- which file provides it.
|
||||
-- @param name string: package name.
|
||||
-- @param version string: package version.
|
||||
-- @param item_type string: "module" or "command".
|
||||
-- @param item_name string: module or command name.
|
||||
-- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used.
|
||||
-- @return (string, string): rock manifest subtree the file comes from ("bin", "lua", or "lib")
|
||||
-- and path to the providing file relatively to that subtree.
|
||||
function manif.get_providing_file(name, version, item_type, item_name, repo)
|
||||
local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
|
||||
local manifest = manif_core.load_local_manifest(rocks_dir)
|
||||
|
||||
local entry_table = manifest.repository[name][version][1]
|
||||
local file_path = entry_table[item_type .. "s"][item_name]
|
||||
|
||||
if item_type == "command" then
|
||||
return "bin", file_path
|
||||
end
|
||||
|
||||
-- A module can be in "lua" or "lib". Decide based on extension first:
|
||||
-- most likely Lua modules are in "lua/" and C modules are in "lib/".
|
||||
if file_path:match("%." .. cfg.lua_extension .. "$") then
|
||||
return "lua", file_path
|
||||
elseif file_path:match("%." .. cfg.lib_extension .. "$") then
|
||||
return "lib", file_path
|
||||
end
|
||||
|
||||
-- Fallback to rock manifest scanning.
|
||||
local rock_manifest = manif.load_rock_manifest(name, version)
|
||||
local subtree = rock_manifest.lib
|
||||
|
||||
for path_part in file_path:gmatch("[^/]+") do
|
||||
if type(subtree) == "table" then
|
||||
subtree = subtree[path_part]
|
||||
else
|
||||
-- Assume it's in "lua/" if it's not in "lib/".
|
||||
return "lua", file_path
|
||||
end
|
||||
end
|
||||
|
||||
return type(subtree) == "string" and "lib" or "lua", file_path
|
||||
end
|
||||
|
||||
return manif
|
||||
106
Utils/luarocks/lua/luarocks/manif_core.lua
Normal file
106
Utils/luarocks/lua/luarocks/manif_core.lua
Normal file
@ -0,0 +1,106 @@
|
||||
|
||||
--- Core functions for querying manifest files.
|
||||
-- This module requires no specific 'fs' functionality.
|
||||
local manif_core = {}
|
||||
package.loaded["luarocks.manif_core"] = manif_core
|
||||
|
||||
local persist = require("luarocks.persist")
|
||||
local type_check = require("luarocks.type_check")
|
||||
local cfg = require("luarocks.cfg")
|
||||
local dir = require("luarocks.dir")
|
||||
local util = require("luarocks.util")
|
||||
local path = require("luarocks.path")
|
||||
|
||||
-- Table with repository identifiers as keys and tables mapping
|
||||
-- Lua versions to cached loaded manifests as values.
|
||||
local manifest_cache = {}
|
||||
|
||||
--- Cache a loaded manifest.
|
||||
-- @param repo_url string: The repository identifier.
|
||||
-- @param lua_version string: Lua version in "5.x" format, defaults to installed version.
|
||||
-- @param manifest table: the manifest to be cached.
|
||||
function manif_core.cache_manifest(repo_url, lua_version, manifest)
|
||||
lua_version = lua_version or cfg.lua_version
|
||||
manifest_cache[repo_url] = manifest_cache[repo_url] or {}
|
||||
manifest_cache[repo_url][lua_version] = manifest
|
||||
end
|
||||
|
||||
--- Attempt to get cached loaded manifest.
|
||||
-- @param repo_url string: The repository identifier.
|
||||
-- @param lua_version string: Lua version in "5.x" format, defaults to installed version.
|
||||
-- @return table or nil: loaded manifest or nil if cache is empty.
|
||||
function manif_core.get_cached_manifest(repo_url, lua_version)
|
||||
lua_version = lua_version or cfg.lua_version
|
||||
return manifest_cache[repo_url] and manifest_cache[repo_url][lua_version]
|
||||
end
|
||||
|
||||
--- Back-end function that actually loads the manifest
|
||||
-- and stores it in the manifest cache.
|
||||
-- @param file string: The local filename of the manifest file.
|
||||
-- @param repo_url string: The repository identifier.
|
||||
-- @param lua_version string: Lua version in "5.x" format, defaults to installed version.
|
||||
-- @param quick boolean: If given, skips type checking.
|
||||
-- @return table or (nil, string, string): the manifest or nil,
|
||||
-- error message and error code ("open", "load", "run" or "type").
|
||||
function manif_core.manifest_loader(file, repo_url, lua_version, quick)
|
||||
local manifest, err, errcode = persist.load_into_table(file)
|
||||
if not manifest then
|
||||
return nil, "Failed loading manifest for "..repo_url..": "..err, errcode
|
||||
end
|
||||
local globals = err
|
||||
if not quick then
|
||||
local ok, err = type_check.type_check_manifest(manifest, globals)
|
||||
if not ok then
|
||||
return nil, "Error checking manifest: "..err, "type"
|
||||
end
|
||||
end
|
||||
|
||||
manif_core.cache_manifest(repo_url, lua_version, manifest)
|
||||
return manifest
|
||||
end
|
||||
|
||||
--- Load a local manifest describing a repository.
|
||||
-- All functions that use manifest tables assume they were obtained
|
||||
-- through either this function or load_manifest.
|
||||
-- @param repo_url string: URL or pathname for the repository.
|
||||
-- @return table or (nil, string, string): A table representing the manifest,
|
||||
-- or nil followed by an error message and an error code, see manifest_loader.
|
||||
function manif_core.load_local_manifest(repo_url)
|
||||
assert(type(repo_url) == "string")
|
||||
|
||||
local cached_manifest = manif_core.get_cached_manifest(repo_url)
|
||||
if cached_manifest then
|
||||
return cached_manifest
|
||||
end
|
||||
|
||||
local pathname = dir.path(repo_url, "manifest")
|
||||
return manif_core.manifest_loader(pathname, repo_url, nil, true)
|
||||
end
|
||||
|
||||
--- Get all versions of a package listed in a manifest file.
|
||||
-- @param name string: a package name.
|
||||
-- @param deps_mode string: "one", to use only the currently
|
||||
-- configured tree; "order" to select trees based on order
|
||||
-- (use the current tree and all trees below it on the list)
|
||||
-- or "all", to use all trees.
|
||||
-- @return table: An array of strings listing installed
|
||||
-- versions of a package.
|
||||
function manif_core.get_versions(name, deps_mode)
|
||||
assert(type(name) == "string")
|
||||
assert(type(deps_mode) == "string")
|
||||
|
||||
local version_set = {}
|
||||
path.map_trees(deps_mode, function(tree)
|
||||
local manifest = manif_core.load_local_manifest(path.rocks_dir(tree))
|
||||
|
||||
if manifest and manifest.repository[name] then
|
||||
for version in pairs(manifest.repository[name]) do
|
||||
version_set[version] = true
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
return util.keys(version_set)
|
||||
end
|
||||
|
||||
return manif_core
|
||||
200
Utils/luarocks/lua/luarocks/new_version.lua
Normal file
200
Utils/luarocks/lua/luarocks/new_version.lua
Normal file
@ -0,0 +1,200 @@
|
||||
|
||||
--- Module implementing the LuaRocks "new_version" command.
|
||||
-- Utility function that writes a new rockspec, updating data from a previous one.
|
||||
local new_version = {}
|
||||
|
||||
local util = require("luarocks.util")
|
||||
local download = require("luarocks.download")
|
||||
local fetch = require("luarocks.fetch")
|
||||
local persist = require("luarocks.persist")
|
||||
local fs = require("luarocks.fs")
|
||||
local type_check = require("luarocks.type_check")
|
||||
|
||||
util.add_run_function(new_version)
|
||||
new_version.help_summary = "Auto-write a rockspec for a new version of a rock."
|
||||
new_version.help_arguments = "[--tag=<tag>] [<package>|<rockspec>] [<new_version>] [<new_url>]"
|
||||
new_version.help = [[
|
||||
This is a utility function that writes a new rockspec, updating data
|
||||
from a previous one.
|
||||
|
||||
If a package name is given, it downloads the latest rockspec from the
|
||||
default server. If a rockspec is given, it uses it instead. If no argument
|
||||
is given, it looks for a rockspec same way 'luarocks make' does.
|
||||
|
||||
If the version number is not given and tag is passed using --tag,
|
||||
it is used as the version, with 'v' removed from beginning.
|
||||
Otherwise, it only increments the revision number of the given
|
||||
(or downloaded) rockspec.
|
||||
|
||||
If a URL is given, it replaces the one from the old rockspec with the
|
||||
given URL. If a URL is not given and a new version is given, it tries
|
||||
to guess the new URL by replacing occurrences of the version number
|
||||
in the URL or tag. It also tries to download the new URL to determine
|
||||
the new MD5 checksum.
|
||||
|
||||
If a tag is given, it replaces the one from the old rockspec. If there is
|
||||
an old tag but no new one passed, it is guessed in the same way URL is.
|
||||
|
||||
WARNING: it writes the new rockspec to the current directory,
|
||||
overwriting the file if it already exists.
|
||||
]]
|
||||
|
||||
local function try_replace(tbl, field, old, new)
|
||||
if not tbl[field] then
|
||||
return false
|
||||
end
|
||||
local old_field = tbl[field]
|
||||
local new_field = tbl[field]:gsub(old, new)
|
||||
if new_field ~= old_field then
|
||||
util.printout("Guessing new '"..field.."' field as "..new_field)
|
||||
tbl[field] = new_field
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Try to download source file using URL from a rockspec.
|
||||
-- If it specified MD5, update it.
|
||||
-- @return (true, false) if MD5 was not specified or it stayed same,
|
||||
-- (true, true) if MD5 changed, (nil, string) on error.
|
||||
local function check_url_and_update_md5(out_rs)
|
||||
local file, temp_dir = fetch.fetch_url_at_temp_dir(out_rs.source.url, "luarocks-new-version-"..out_rs.package)
|
||||
if not file then
|
||||
util.printerr("Warning: invalid URL - "..temp_dir)
|
||||
return true, false
|
||||
end
|
||||
|
||||
local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, out_rs.source.url, out_rs.source.dir)
|
||||
if not inferred_dir then
|
||||
return nil, found_dir
|
||||
end
|
||||
|
||||
if found_dir and found_dir ~= inferred_dir then
|
||||
out_rs.source.dir = found_dir
|
||||
end
|
||||
|
||||
if file then
|
||||
if out_rs.source.md5 then
|
||||
util.printout("File successfully downloaded. Updating MD5 checksum...")
|
||||
local new_md5, err = fs.get_md5(file)
|
||||
if not new_md5 then
|
||||
return nil, err
|
||||
end
|
||||
local old_md5 = out_rs.source.md5
|
||||
out_rs.source.md5 = new_md5
|
||||
return true, new_md5 ~= old_md5
|
||||
else
|
||||
util.printout("File successfully downloaded.")
|
||||
return true, false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function update_source_section(out_rs, url, tag, old_ver, new_ver)
|
||||
if tag then
|
||||
out_rs.source.tag = tag
|
||||
end
|
||||
if url then
|
||||
out_rs.source.url = url
|
||||
return check_url_and_update_md5(out_rs)
|
||||
end
|
||||
if new_ver == old_ver then
|
||||
return true
|
||||
end
|
||||
if out_rs.source.dir then
|
||||
try_replace(out_rs.source, "dir", old_ver, new_ver)
|
||||
end
|
||||
if out_rs.source.file then
|
||||
try_replace(out_rs.source, "file", old_ver, new_ver)
|
||||
end
|
||||
if try_replace(out_rs.source, "url", old_ver, new_ver) then
|
||||
return check_url_and_update_md5(out_rs)
|
||||
end
|
||||
if tag or try_replace(out_rs.source, "tag", old_ver, new_ver) then
|
||||
return true
|
||||
end
|
||||
-- Couldn't replace anything significant, use the old URL.
|
||||
local ok, md5_changed = check_url_and_update_md5(out_rs)
|
||||
if not ok then
|
||||
return nil, md5_changed
|
||||
end
|
||||
if md5_changed then
|
||||
util.printerr("Warning: URL is the same, but MD5 has changed. Old rockspec is broken.")
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function new_version.command(flags, input, version, url)
|
||||
if not input then
|
||||
local err
|
||||
input, err = util.get_default_rockspec()
|
||||
if not input then
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
assert(type(input) == "string")
|
||||
|
||||
local filename, err
|
||||
if input:match("rockspec$") then
|
||||
filename, err = fetch.fetch_url(input)
|
||||
if not filename then
|
||||
return nil, err
|
||||
end
|
||||
else
|
||||
filename, err = download.download("rockspec", input:lower())
|
||||
if not filename then
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
|
||||
local valid_rs, err = fetch.load_rockspec(filename)
|
||||
if not valid_rs then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local old_ver, old_rev = valid_rs.version:match("(.*)%-(%d+)$")
|
||||
local new_ver, new_rev
|
||||
|
||||
if flags.tag and not version then
|
||||
version = flags.tag:gsub("^v", "")
|
||||
end
|
||||
|
||||
if version then
|
||||
new_ver, new_rev = version:match("(.*)%-(%d+)$")
|
||||
new_rev = tonumber(new_rev)
|
||||
if not new_rev then
|
||||
new_ver = version
|
||||
new_rev = 1
|
||||
end
|
||||
else
|
||||
new_ver = old_ver
|
||||
new_rev = tonumber(old_rev) + 1
|
||||
end
|
||||
local new_rockver = new_ver:gsub("-", "")
|
||||
|
||||
local out_rs, err = persist.load_into_table(filename)
|
||||
local out_name = out_rs.package:lower()
|
||||
out_rs.version = new_rockver.."-"..new_rev
|
||||
|
||||
local ok, err = update_source_section(out_rs, url, flags.tag, old_ver, new_ver)
|
||||
if not ok then return nil, err end
|
||||
|
||||
if out_rs.build and out_rs.build.type == "module" then
|
||||
out_rs.build.type = "builtin"
|
||||
end
|
||||
|
||||
local out_filename = out_name.."-"..new_rockver.."-"..new_rev..".rockspec"
|
||||
|
||||
persist.save_from_table(out_filename, out_rs, type_check.rockspec_order)
|
||||
|
||||
util.printout("Wrote "..out_filename)
|
||||
|
||||
local valid_out_rs, err = fetch.load_local_rockspec(out_filename)
|
||||
if not valid_out_rs then
|
||||
return nil, "Failed loading generated rockspec: "..err
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return new_version
|
||||
197
Utils/luarocks/lua/luarocks/pack.lua
Normal file
197
Utils/luarocks/lua/luarocks/pack.lua
Normal file
@ -0,0 +1,197 @@
|
||||
|
||||
--- Module implementing the LuaRocks "pack" command.
|
||||
-- Creates a rock, packing sources or binaries.
|
||||
local pack = {}
|
||||
package.loaded["luarocks.pack"] = pack
|
||||
|
||||
local unpack = unpack or table.unpack
|
||||
|
||||
local path = require("luarocks.path")
|
||||
local repos = require("luarocks.repos")
|
||||
local fetch = require("luarocks.fetch")
|
||||
local fs = require("luarocks.fs")
|
||||
local cfg = require("luarocks.cfg")
|
||||
local util = require("luarocks.util")
|
||||
local dir = require("luarocks.dir")
|
||||
local manif = require("luarocks.manif")
|
||||
local search = require("luarocks.search")
|
||||
|
||||
util.add_run_function(pack)
|
||||
pack.help_summary = "Create a rock, packing sources or binaries."
|
||||
pack.help_arguments = "{<rockspec>|<name> [<version>]}"
|
||||
pack.help = [[
|
||||
Argument may be a rockspec file, for creating a source rock,
|
||||
or the name of an installed package, for creating a binary rock.
|
||||
In the latter case, the app version may be given as a second
|
||||
argument.
|
||||
]]
|
||||
|
||||
--- Create a source rock.
|
||||
-- Packages a rockspec and its required source files in a rock
|
||||
-- file with the .src.rock extension, which can later be built and
|
||||
-- installed with the "build" command.
|
||||
-- @param rockspec_file string: An URL or pathname for a rockspec file.
|
||||
-- @return string or (nil, string): The filename of the resulting
|
||||
-- .src.rock file; or nil and an error message.
|
||||
function pack.pack_source_rock(rockspec_file)
|
||||
assert(type(rockspec_file) == "string")
|
||||
|
||||
local rockspec, err = fetch.load_rockspec(rockspec_file)
|
||||
if err then
|
||||
return nil, "Error loading rockspec: "..err
|
||||
end
|
||||
rockspec_file = rockspec.local_filename
|
||||
|
||||
local name_version = rockspec.name .. "-" .. rockspec.version
|
||||
local rock_file = fs.absolute_name(name_version .. ".src.rock")
|
||||
|
||||
local source_file, source_dir = fetch.fetch_sources(rockspec, false)
|
||||
if not source_file then
|
||||
return nil, source_dir
|
||||
end
|
||||
local ok, err = fs.change_dir(source_dir)
|
||||
if not ok then return nil, err end
|
||||
|
||||
fs.delete(rock_file)
|
||||
fs.copy(rockspec_file, source_dir, cfg.perm_read)
|
||||
if not fs.zip(rock_file, dir.base_name(rockspec_file), dir.base_name(source_file)) then
|
||||
return nil, "Failed packing "..rock_file
|
||||
end
|
||||
fs.pop_dir()
|
||||
|
||||
return rock_file
|
||||
end
|
||||
|
||||
local function copy_back_files(name, version, file_tree, deploy_dir, pack_dir, perms)
|
||||
local ok, err = fs.make_dir(pack_dir)
|
||||
if not ok then return nil, err end
|
||||
for file, sub in pairs(file_tree) do
|
||||
local source = dir.path(deploy_dir, file)
|
||||
local target = dir.path(pack_dir, file)
|
||||
if type(sub) == "table" then
|
||||
local ok, err = copy_back_files(name, version, sub, source, target)
|
||||
if not ok then return nil, err end
|
||||
else
|
||||
local versioned = path.versioned_name(source, deploy_dir, name, version)
|
||||
if fs.exists(versioned) then
|
||||
fs.copy(versioned, target, perms)
|
||||
else
|
||||
fs.copy(source, target, perms)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- @param name string: Name of package to pack.
|
||||
-- @param version string or nil: A version number may also be passed.
|
||||
-- @param tree string or nil: An optional tree to pick the package from.
|
||||
-- @return string or (nil, string): The filename of the resulting
|
||||
-- .src.rock file; or nil and an error message.
|
||||
local function do_pack_binary_rock(name, version, tree)
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string" or not version)
|
||||
|
||||
local repo, repo_url
|
||||
name, version, repo, repo_url = search.pick_installed_rock(name, version, tree)
|
||||
if not name then
|
||||
return nil, version
|
||||
end
|
||||
|
||||
local root = path.root_dir(repo_url)
|
||||
local prefix = path.install_dir(name, version, root)
|
||||
if not fs.exists(prefix) then
|
||||
return nil, "'"..name.." "..version.."' does not seem to be an installed rock."
|
||||
end
|
||||
|
||||
local rock_manifest = manif.load_rock_manifest(name, version, root)
|
||||
if not rock_manifest then
|
||||
return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?"
|
||||
end
|
||||
|
||||
local name_version = name .. "-" .. version
|
||||
local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock")
|
||||
|
||||
local temp_dir = fs.make_temp_dir("pack")
|
||||
fs.copy_contents(prefix, temp_dir)
|
||||
|
||||
local is_binary = false
|
||||
if rock_manifest.lib then
|
||||
local ok, err = copy_back_files(name, version, rock_manifest.lib, path.deploy_lib_dir(root), dir.path(temp_dir, "lib"), cfg.perm_exec)
|
||||
if not ok then return nil, "Failed copying back files: " .. err end
|
||||
is_binary = true
|
||||
end
|
||||
if rock_manifest.lua then
|
||||
local ok, err = copy_back_files(name, version, rock_manifest.lua, path.deploy_lua_dir(root), dir.path(temp_dir, "lua"), cfg.perm_read)
|
||||
if not ok then return nil, "Failed copying back files: " .. err end
|
||||
end
|
||||
|
||||
local ok, err = fs.change_dir(temp_dir)
|
||||
if not ok then return nil, err end
|
||||
if not is_binary and not repos.has_binaries(name, version) then
|
||||
rock_file = rock_file:gsub("%."..cfg.arch:gsub("%-","%%-").."%.", ".all.")
|
||||
end
|
||||
fs.delete(rock_file)
|
||||
if not fs.zip(rock_file, unpack(fs.list_dir())) then
|
||||
return nil, "Failed packing "..rock_file
|
||||
end
|
||||
fs.pop_dir()
|
||||
fs.delete(temp_dir)
|
||||
return rock_file
|
||||
end
|
||||
|
||||
function pack.pack_binary_rock(name, version, cmd, ...)
|
||||
|
||||
-- The --pack-binary-rock option for "luarocks build" basically performs
|
||||
-- "luarocks build" on a temporary tree and then "luarocks pack". The
|
||||
-- alternative would require refactoring parts of luarocks.build and
|
||||
-- luarocks.pack, which would save a few file operations: the idea would be
|
||||
-- to shave off the final deploy steps from the build phase and the initial
|
||||
-- collect steps from the pack phase.
|
||||
|
||||
local temp_dir, err = fs.make_temp_dir("luarocks-build-pack-"..dir.base_name(name))
|
||||
if not temp_dir then
|
||||
return nil, "Failed creating temporary directory: "..err
|
||||
end
|
||||
util.schedule_function(fs.delete, temp_dir)
|
||||
|
||||
path.use_tree(temp_dir)
|
||||
local ok, err = cmd(...)
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
local rname, rversion = path.parse_name(name)
|
||||
if not rname then
|
||||
rname, rversion = name, version
|
||||
end
|
||||
return do_pack_binary_rock(rname, rversion, temp_dir)
|
||||
end
|
||||
|
||||
--- Driver function for the "pack" command.
|
||||
-- @param arg string: may be a rockspec file, for creating a source rock,
|
||||
-- or the name of an installed package, for creating a binary rock.
|
||||
-- @param version string or nil: if the name of a package is given, a
|
||||
-- version may also be passed.
|
||||
-- @return boolean or (nil, string): true if successful or nil followed
|
||||
-- by an error message.
|
||||
function pack.command(flags, arg, version)
|
||||
assert(type(version) == "string" or not version)
|
||||
if type(arg) ~= "string" then
|
||||
return nil, "Argument missing. "..util.see_help("pack")
|
||||
end
|
||||
|
||||
local file, err
|
||||
if arg:match(".*%.rockspec") then
|
||||
file, err = pack.pack_source_rock(arg)
|
||||
else
|
||||
file, err = do_pack_binary_rock(arg:lower(), version, flags["tree"])
|
||||
end
|
||||
if err then
|
||||
return nil, err
|
||||
else
|
||||
util.printout("Packed: "..file)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return pack
|
||||
388
Utils/luarocks/lua/luarocks/path.lua
Normal file
388
Utils/luarocks/lua/luarocks/path.lua
Normal file
@ -0,0 +1,388 @@
|
||||
|
||||
--- LuaRocks-specific path handling functions.
|
||||
-- All paths are configured in this module, making it a single
|
||||
-- point where the layout of the local installation is defined in LuaRocks.
|
||||
local path = {}
|
||||
|
||||
local dir = require("luarocks.dir")
|
||||
local cfg = require("luarocks.cfg")
|
||||
local util = require("luarocks.util")
|
||||
|
||||
--- Infer rockspec filename from a rock filename.
|
||||
-- @param rock_name string: Pathname of a rock file.
|
||||
-- @return string: Filename of the rockspec, without path.
|
||||
function path.rockspec_name_from_rock(rock_name)
|
||||
assert(type(rock_name) == "string")
|
||||
local base_name = dir.base_name(rock_name)
|
||||
return base_name:match("(.*)%.[^.]*.rock") .. ".rockspec"
|
||||
end
|
||||
|
||||
function path.rocks_dir(tree)
|
||||
if type(tree) == "string" then
|
||||
return dir.path(tree, cfg.rocks_subdir)
|
||||
else
|
||||
assert(type(tree) == "table")
|
||||
return tree.rocks_dir or dir.path(tree.root, cfg.rocks_subdir)
|
||||
end
|
||||
end
|
||||
|
||||
function path.root_dir(rocks_dir)
|
||||
assert(type(rocks_dir) == "string")
|
||||
return rocks_dir:match("(.*)" .. util.matchquote(cfg.rocks_subdir) .. ".*$")
|
||||
end
|
||||
|
||||
function path.rocks_tree_to_string(tree)
|
||||
if type(tree) == "string" then
|
||||
return tree
|
||||
else
|
||||
assert(type(tree) == "table")
|
||||
return tree.root
|
||||
end
|
||||
end
|
||||
|
||||
function path.deploy_bin_dir(tree)
|
||||
if type(tree) == "string" then
|
||||
return dir.path(tree, "bin")
|
||||
else
|
||||
assert(type(tree) == "table")
|
||||
return tree.bin_dir or dir.path(tree.root, "bin")
|
||||
end
|
||||
end
|
||||
|
||||
function path.deploy_lua_dir(tree)
|
||||
if type(tree) == "string" then
|
||||
return dir.path(tree, cfg.lua_modules_path)
|
||||
else
|
||||
assert(type(tree) == "table")
|
||||
return tree.lua_dir or dir.path(tree.root, cfg.lua_modules_path)
|
||||
end
|
||||
end
|
||||
|
||||
function path.deploy_lib_dir(tree)
|
||||
if type(tree) == "string" then
|
||||
return dir.path(tree, cfg.lib_modules_path)
|
||||
else
|
||||
assert(type(tree) == "table")
|
||||
return tree.lib_dir or dir.path(tree.root, cfg.lib_modules_path)
|
||||
end
|
||||
end
|
||||
|
||||
function path.manifest_file(tree)
|
||||
if type(tree) == "string" then
|
||||
return dir.path(tree, cfg.rocks_subdir, "manifest")
|
||||
else
|
||||
assert(type(tree) == "table")
|
||||
return (tree.rocks_dir and dir.path(tree.rocks_dir, "manifest")) or dir.path(tree.root, cfg.rocks_subdir, "manifest")
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the directory for all versions of a package in a tree.
|
||||
-- @param name string: The package name.
|
||||
-- @return string: The resulting path -- does not guarantee that
|
||||
-- @param tree string or nil: If given, specifies the local tree to use.
|
||||
-- the package (and by extension, the path) exists.
|
||||
function path.versions_dir(name, tree)
|
||||
assert(type(name) == "string")
|
||||
tree = tree or cfg.root_dir
|
||||
return dir.path(path.rocks_dir(tree), name)
|
||||
end
|
||||
|
||||
--- Get the local installation directory (prefix) for a package.
|
||||
-- @param name string: The package name.
|
||||
-- @param version string: The package version.
|
||||
-- @param tree string or nil: If given, specifies the local tree to use.
|
||||
-- @return string: The resulting path -- does not guarantee that
|
||||
-- the package (and by extension, the path) exists.
|
||||
function path.install_dir(name, version, tree)
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
tree = tree or cfg.root_dir
|
||||
return dir.path(path.rocks_dir(tree), name, version)
|
||||
end
|
||||
|
||||
--- Get the local filename of the rockspec of an installed rock.
|
||||
-- @param name string: The package name.
|
||||
-- @param version string: The package version.
|
||||
-- @param tree string or nil: If given, specifies the local tree to use.
|
||||
-- @return string: The resulting path -- does not guarantee that
|
||||
-- the package (and by extension, the file) exists.
|
||||
function path.rockspec_file(name, version, tree)
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
tree = tree or cfg.root_dir
|
||||
return dir.path(path.rocks_dir(tree), name, version, name.."-"..version..".rockspec")
|
||||
end
|
||||
|
||||
--- Get the local filename of the rock_manifest file of an installed rock.
|
||||
-- @param name string: The package name.
|
||||
-- @param version string: The package version.
|
||||
-- @param tree string or nil: If given, specifies the local tree to use.
|
||||
-- @return string: The resulting path -- does not guarantee that
|
||||
-- the package (and by extension, the file) exists.
|
||||
function path.rock_manifest_file(name, version, tree)
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
tree = tree or cfg.root_dir
|
||||
return dir.path(path.rocks_dir(tree), name, version, "rock_manifest")
|
||||
end
|
||||
|
||||
--- Get the local installation directory for C libraries of a package.
|
||||
-- @param name string: The package name.
|
||||
-- @param version string: The package version.
|
||||
-- @param tree string or nil: If given, specifies the local tree to use.
|
||||
-- @return string: The resulting path -- does not guarantee that
|
||||
-- the package (and by extension, the path) exists.
|
||||
function path.lib_dir(name, version, tree)
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
tree = tree or cfg.root_dir
|
||||
return dir.path(path.rocks_dir(tree), name, version, "lib")
|
||||
end
|
||||
|
||||
--- Get the local installation directory for Lua modules of a package.
|
||||
-- @param name string: The package name.
|
||||
-- @param version string: The package version.
|
||||
-- @param tree string or nil: If given, specifies the local tree to use.
|
||||
-- @return string: The resulting path -- does not guarantee that
|
||||
-- the package (and by extension, the path) exists.
|
||||
function path.lua_dir(name, version, tree)
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
tree = tree or cfg.root_dir
|
||||
return dir.path(path.rocks_dir(tree), name, version, "lua")
|
||||
end
|
||||
|
||||
--- Get the local installation directory for documentation of a package.
|
||||
-- @param name string: The package name.
|
||||
-- @param version string: The package version.
|
||||
-- @param tree string or nil: If given, specifies the local tree to use.
|
||||
-- @return string: The resulting path -- does not guarantee that
|
||||
-- the package (and by extension, the path) exists.
|
||||
function path.doc_dir(name, version, tree)
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
tree = tree or cfg.root_dir
|
||||
return dir.path(path.rocks_dir(tree), name, version, "doc")
|
||||
end
|
||||
|
||||
--- Get the local installation directory for configuration files of a package.
|
||||
-- @param name string: The package name.
|
||||
-- @param version string: The package version.
|
||||
-- @param tree string or nil: If given, specifies the local tree to use.
|
||||
-- @return string: The resulting path -- does not guarantee that
|
||||
-- the package (and by extension, the path) exists.
|
||||
function path.conf_dir(name, version, tree)
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
tree = tree or cfg.root_dir
|
||||
return dir.path(path.rocks_dir(tree), name, version, "conf")
|
||||
end
|
||||
|
||||
--- Get the local installation directory for command-line scripts
|
||||
-- of a package.
|
||||
-- @param name string: The package name.
|
||||
-- @param version string: The package version.
|
||||
-- @param tree string or nil: If given, specifies the local tree to use.
|
||||
-- @return string: The resulting path -- does not guarantee that
|
||||
-- the package (and by extension, the path) exists.
|
||||
function path.bin_dir(name, version, tree)
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
tree = tree or cfg.root_dir
|
||||
return dir.path(path.rocks_dir(tree), name, version, "bin")
|
||||
end
|
||||
|
||||
--- Extract name, version and arch of a rock filename,
|
||||
-- or name, version and "rockspec" from a rockspec name.
|
||||
-- @param file_name string: pathname of a rock or rockspec
|
||||
-- @return (string, string, string) or nil: name, version and arch
|
||||
-- or nil if name could not be parsed
|
||||
function path.parse_name(file_name)
|
||||
assert(type(file_name) == "string")
|
||||
if file_name:match("%.rock$") then
|
||||
return dir.base_name(file_name):match("(.*)-([^-]+-%d+)%.([^.]+)%.rock$")
|
||||
else
|
||||
return dir.base_name(file_name):match("(.*)-([^-]+-%d+)%.(rockspec)")
|
||||
end
|
||||
end
|
||||
|
||||
--- Make a rockspec or rock URL.
|
||||
-- @param pathname string: Base URL or pathname.
|
||||
-- @param name string: Package name.
|
||||
-- @param version string: Package version.
|
||||
-- @param arch string: Architecture identifier, or "rockspec" or "installed".
|
||||
-- @return string: A URL or pathname following LuaRocks naming conventions.
|
||||
function path.make_url(pathname, name, version, arch)
|
||||
assert(type(pathname) == "string")
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
assert(type(arch) == "string")
|
||||
|
||||
local filename = name.."-"..version
|
||||
if arch == "installed" then
|
||||
filename = dir.path(name, version, filename..".rockspec")
|
||||
elseif arch == "rockspec" then
|
||||
filename = filename..".rockspec"
|
||||
else
|
||||
filename = filename.."."..arch..".rock"
|
||||
end
|
||||
return dir.path(pathname, filename)
|
||||
end
|
||||
|
||||
--- Convert a pathname to a module identifier.
|
||||
-- In Unix, for example, a path "foo/bar/baz.lua" is converted to
|
||||
-- "foo.bar.baz"; "bla/init.lua" returns "bla"; "foo.so" returns "foo".
|
||||
-- @param file string: Pathname of module
|
||||
-- @return string: The module identifier, or nil if given path is
|
||||
-- not a conformant module path (the function does not check if the
|
||||
-- path actually exists).
|
||||
function path.path_to_module(file)
|
||||
assert(type(file) == "string")
|
||||
|
||||
local name = file:match("(.*)%."..cfg.lua_extension.."$")
|
||||
if name then
|
||||
name = name:gsub(dir.separator, ".")
|
||||
local init = name:match("(.*)%.init$")
|
||||
if init then
|
||||
name = init
|
||||
end
|
||||
else
|
||||
name = file:match("(.*)%."..cfg.lib_extension.."$")
|
||||
if name then
|
||||
name = name:gsub(dir.separator, ".")
|
||||
end
|
||||
end
|
||||
if not name then name = file end
|
||||
name = name:gsub("^%.+", ""):gsub("%.+$", "")
|
||||
return name
|
||||
end
|
||||
|
||||
--- Obtain the directory name where a module should be stored.
|
||||
-- For example, on Unix, "foo.bar.baz" will return "foo/bar".
|
||||
-- @param mod string: A module name in Lua dot-separated format.
|
||||
-- @return string: A directory name using the platform's separator.
|
||||
function path.module_to_path(mod)
|
||||
assert(type(mod) == "string")
|
||||
return (mod:gsub("[^.]*$", ""):gsub("%.", dir.separator))
|
||||
end
|
||||
|
||||
--- Set up path-related variables for a given rock.
|
||||
-- Create a "variables" table in the rockspec table, containing
|
||||
-- adjusted variables according to the configuration file.
|
||||
-- @param rockspec table: The rockspec table.
|
||||
function path.configure_paths(rockspec)
|
||||
assert(type(rockspec) == "table")
|
||||
local vars = {}
|
||||
for k,v in pairs(cfg.variables) do
|
||||
vars[k] = v
|
||||
end
|
||||
local name, version = rockspec.name, rockspec.version
|
||||
vars.PREFIX = path.install_dir(name, version)
|
||||
vars.LUADIR = path.lua_dir(name, version)
|
||||
vars.LIBDIR = path.lib_dir(name, version)
|
||||
vars.CONFDIR = path.conf_dir(name, version)
|
||||
vars.BINDIR = path.bin_dir(name, version)
|
||||
vars.DOCDIR = path.doc_dir(name, version)
|
||||
rockspec.variables = vars
|
||||
end
|
||||
|
||||
--- Produce a versioned version of a filename.
|
||||
-- @param file string: filename (must start with prefix)
|
||||
-- @param prefix string: Path prefix for file
|
||||
-- @param name string: Rock name
|
||||
-- @param version string: Rock version
|
||||
-- @return string: a pathname with the same directory parts and a versioned basename.
|
||||
function path.versioned_name(file, prefix, name, version)
|
||||
assert(type(file) == "string")
|
||||
assert(type(name) == "string")
|
||||
assert(type(version) == "string")
|
||||
|
||||
local rest = file:sub(#prefix+1):gsub("^/*", "")
|
||||
local name_version = (name.."_"..version):gsub("%-", "_"):gsub("%.", "_")
|
||||
return dir.path(prefix, name_version.."-"..rest)
|
||||
end
|
||||
|
||||
function path.use_tree(tree)
|
||||
cfg.root_dir = tree
|
||||
cfg.rocks_dir = path.rocks_dir(tree)
|
||||
cfg.deploy_bin_dir = path.deploy_bin_dir(tree)
|
||||
cfg.deploy_lua_dir = path.deploy_lua_dir(tree)
|
||||
cfg.deploy_lib_dir = path.deploy_lib_dir(tree)
|
||||
end
|
||||
|
||||
--- Apply a given function to the active rocks trees based on chosen dependency mode.
|
||||
-- @param deps_mode string: Dependency mode: "one" for the current default tree,
|
||||
-- "all" for all trees, "order" for all trees with priority >= the current default,
|
||||
-- "none" for no trees (this function becomes a nop).
|
||||
-- @param fn function: function to be applied, with the tree dir (string) as the first
|
||||
-- argument and the remaining varargs of map_trees as the following arguments.
|
||||
-- @return a table with all results of invocations of fn collected.
|
||||
function path.map_trees(deps_mode, fn, ...)
|
||||
local result = {}
|
||||
if deps_mode == "one" then
|
||||
table.insert(result, (fn(cfg.root_dir, ...)) or 0)
|
||||
elseif deps_mode == "all" or deps_mode == "order" then
|
||||
local use = false
|
||||
if deps_mode == "all" then
|
||||
use = true
|
||||
end
|
||||
for _, tree in ipairs(cfg.rocks_trees) do
|
||||
if dir.normalize(path.rocks_tree_to_string(tree)) == dir.normalize(path.rocks_tree_to_string(cfg.root_dir)) then
|
||||
use = true
|
||||
end
|
||||
if use then
|
||||
table.insert(result, (fn(tree, ...)) or 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local is_src_extension = { [".lua"] = true, [".tl"] = true, [".tld"] = true, [".moon"] = true }
|
||||
|
||||
--- Return the pathname of the file that would be loaded for a module, indexed.
|
||||
-- @param file_name string: module file name as in manifest (eg. "socket/core.so")
|
||||
-- @param name string: name of the package (eg. "luasocket")
|
||||
-- @param version string: version number (eg. "2.0.2-1")
|
||||
-- @param tree string: repository path (eg. "/usr/local")
|
||||
-- @param i number: the index, 1 if version is the current default, > 1 otherwise.
|
||||
-- This is done this way for use by select_module in luarocks.loader.
|
||||
-- @return string: filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so")
|
||||
function path.which_i(file_name, name, version, tree, i)
|
||||
local deploy_dir
|
||||
local extension = file_name:match("%.[a-z]+$")
|
||||
if is_src_extension[extension] then
|
||||
deploy_dir = path.deploy_lua_dir(tree)
|
||||
file_name = dir.path(deploy_dir, file_name)
|
||||
else
|
||||
deploy_dir = path.deploy_lib_dir(tree)
|
||||
file_name = dir.path(deploy_dir, file_name)
|
||||
end
|
||||
if i > 1 then
|
||||
file_name = path.versioned_name(file_name, deploy_dir, name, version)
|
||||
end
|
||||
return file_name
|
||||
end
|
||||
|
||||
--- Return the pathname of the file that would be loaded for a module,
|
||||
-- returning the versioned pathname if given version is not the default version
|
||||
-- in the given manifest.
|
||||
-- @param module_name string: module name (eg. "socket.core")
|
||||
-- @param file_name string: module file name as in manifest (eg. "socket/core.so")
|
||||
-- @param name string: name of the package (eg. "luasocket")
|
||||
-- @param version string: version number (eg. "2.0.2-1")
|
||||
-- @param tree string: repository path (eg. "/usr/local")
|
||||
-- @param manifest table: the manifest table for the tree.
|
||||
-- @return string: filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so")
|
||||
function path.which(module_name, file_name, name, version, tree, manifest)
|
||||
local versions = manifest.modules[module_name]
|
||||
assert(versions)
|
||||
for i, name_version in ipairs(versions) do
|
||||
if name_version == name.."/"..version then
|
||||
return path.which_i(file_name, name, version, tree, i):gsub("//", "/")
|
||||
end
|
||||
end
|
||||
assert(false)
|
||||
end
|
||||
|
||||
return path
|
||||
69
Utils/luarocks/lua/luarocks/path_cmd.lua
Normal file
69
Utils/luarocks/lua/luarocks/path_cmd.lua
Normal file
@ -0,0 +1,69 @@
|
||||
|
||||
--- @module luarocks.path_cmd
|
||||
-- Driver for the `luarocks path` command.
|
||||
local path_cmd = {}
|
||||
|
||||
local util = require("luarocks.util")
|
||||
local cfg = require("luarocks.cfg")
|
||||
|
||||
util.add_run_function(path_cmd)
|
||||
path_cmd.help_summary = "Return the currently configured package path."
|
||||
path_cmd.help_arguments = ""
|
||||
path_cmd.help = [[
|
||||
Returns the package path currently configured for this installation
|
||||
of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH.
|
||||
|
||||
--bin Adds the system path to the output
|
||||
|
||||
--append Appends the paths to the existing paths. Default is to prefix
|
||||
the LR paths to the existing paths.
|
||||
|
||||
--lr-path Exports the Lua path (not formatted as shell command)
|
||||
|
||||
--lr-cpath Exports the Lua cpath (not formatted as shell command)
|
||||
|
||||
--lr-bin Exports the system path (not formatted as shell command)
|
||||
|
||||
|
||||
On Unix systems, you may run:
|
||||
eval `luarocks path`
|
||||
And on Windows:
|
||||
luarocks path > "%temp%\_lrp.bat" && call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat"
|
||||
]]
|
||||
|
||||
--- Driver function for "path" command.
|
||||
-- @return boolean This function always succeeds.
|
||||
function path_cmd.command(flags)
|
||||
local lr_path, lr_cpath, lr_bin = cfg.package_paths(flags["tree"])
|
||||
local path_sep = cfg.export_path_separator
|
||||
|
||||
if flags["lr-path"] then
|
||||
util.printout(util.remove_path_dupes(lr_path, ';'))
|
||||
return true
|
||||
elseif flags["lr-cpath"] then
|
||||
util.printout(util.remove_path_dupes(lr_cpath, ';'))
|
||||
return true
|
||||
elseif flags["lr-bin"] then
|
||||
util.printout(util.remove_path_dupes(lr_bin, path_sep))
|
||||
return true
|
||||
end
|
||||
|
||||
if flags["append"] then
|
||||
lr_path = package.path .. ";" .. lr_path
|
||||
lr_cpath = package.cpath .. ";" .. lr_cpath
|
||||
lr_bin = os.getenv("PATH") .. path_sep .. lr_bin
|
||||
else
|
||||
lr_path = lr_path.. ";" .. package.path
|
||||
lr_cpath = lr_cpath .. ";" .. package.cpath
|
||||
lr_bin = lr_bin .. path_sep .. os.getenv("PATH")
|
||||
end
|
||||
|
||||
util.printout(cfg.export_lua_path:format(util.remove_path_dupes(lr_path, ';')))
|
||||
util.printout(cfg.export_lua_cpath:format(util.remove_path_dupes(lr_cpath, ';')))
|
||||
if flags["bin"] then
|
||||
util.printout(cfg.export_path:format(util.remove_path_dupes(lr_bin, path_sep)))
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return path_cmd
|
||||
210
Utils/luarocks/lua/luarocks/persist.lua
Normal file
210
Utils/luarocks/lua/luarocks/persist.lua
Normal file
@ -0,0 +1,210 @@
|
||||
|
||||
--- Utility module for loading files into tables and
|
||||
-- saving tables into files.
|
||||
-- Implemented separately to avoid interdependencies,
|
||||
-- as it is used in the bootstrapping stage of the cfg module.
|
||||
local persist = {}
|
||||
package.loaded["luarocks.persist"] = persist
|
||||
|
||||
local util = require("luarocks.util")
|
||||
|
||||
--- Load and run a Lua file in an environment.
|
||||
-- @param filename string: the name of the file.
|
||||
-- @param env table: the environment table.
|
||||
-- @return (true, any) or (nil, string, string): true and the return value
|
||||
-- of the file, or nil, an error message and an error code ("open", "load"
|
||||
-- or "run") in case of errors.
|
||||
local function run_file(filename, env)
|
||||
local fd, err = io.open(filename)
|
||||
if not fd then
|
||||
return nil, err, "open"
|
||||
end
|
||||
local str, err = fd:read("*a")
|
||||
fd:close()
|
||||
if not str then
|
||||
return nil, err, "open"
|
||||
end
|
||||
str = str:gsub("^#![^\n]*\n", "")
|
||||
local chunk, ran
|
||||
if _VERSION == "Lua 5.1" then -- Lua 5.1
|
||||
chunk, err = loadstring(str, filename)
|
||||
if chunk then
|
||||
setfenv(chunk, env)
|
||||
ran, err = pcall(chunk)
|
||||
end
|
||||
else -- Lua 5.2
|
||||
chunk, err = load(str, filename, "t", env)
|
||||
if chunk then
|
||||
ran, err = pcall(chunk)
|
||||
end
|
||||
end
|
||||
if not chunk then
|
||||
return nil, "Error loading file: "..err, "load"
|
||||
end
|
||||
if not ran then
|
||||
return nil, "Error running file: "..err, "run"
|
||||
end
|
||||
return true, err
|
||||
end
|
||||
|
||||
--- Load a Lua file containing assignments, storing them in a table.
|
||||
-- The global environment is not propagated to the loaded file.
|
||||
-- @param filename string: the name of the file.
|
||||
-- @param tbl table or nil: if given, this table is used to store
|
||||
-- loaded values.
|
||||
-- @return (table, table) or (nil, string, string): a table with the file's assignments
|
||||
-- as fields and set of undefined globals accessed in file,
|
||||
-- or nil, an error message and an error code ("open"; couldn't open the file,
|
||||
-- "load"; compile-time error, or "run"; run-time error)
|
||||
-- in case of errors.
|
||||
function persist.load_into_table(filename, tbl)
|
||||
assert(type(filename) == "string")
|
||||
assert(type(tbl) == "table" or not tbl)
|
||||
|
||||
local result = tbl or {}
|
||||
local globals = {}
|
||||
local globals_mt = {
|
||||
__index = function(t, k)
|
||||
globals[k] = true
|
||||
end
|
||||
}
|
||||
local save_mt = getmetatable(result)
|
||||
setmetatable(result, globals_mt)
|
||||
|
||||
local ok, err, errcode = run_file(filename, result)
|
||||
|
||||
setmetatable(result, save_mt)
|
||||
|
||||
if not ok then
|
||||
return nil, err, errcode
|
||||
end
|
||||
return result, globals
|
||||
end
|
||||
|
||||
local write_table
|
||||
|
||||
--- Write a value as Lua code.
|
||||
-- This function handles only numbers and strings, invoking write_table
|
||||
-- to write tables.
|
||||
-- @param out table or userdata: a writer object supporting :write() method.
|
||||
-- @param v: the value to be written.
|
||||
-- @param level number: the indentation level
|
||||
-- @param sub_order table: optional prioritization table
|
||||
-- @see write_table
|
||||
local function write_value(out, v, level, sub_order)
|
||||
if type(v) == "table" then
|
||||
write_table(out, v, level + 1, sub_order)
|
||||
elseif type(v) == "string" then
|
||||
if v:match("[\r\n]") then
|
||||
local open, close = "[[", "]]"
|
||||
local equals = 0
|
||||
local v_with_bracket = v.."]"
|
||||
while v_with_bracket:find(close, 1, true) do
|
||||
equals = equals + 1
|
||||
local eqs = ("="):rep(equals)
|
||||
open, close = "["..eqs.."[", "]"..eqs.."]"
|
||||
end
|
||||
out:write(open.."\n"..v..close)
|
||||
else
|
||||
out:write(("%q"):format(v))
|
||||
end
|
||||
else
|
||||
out:write(tostring(v))
|
||||
end
|
||||
end
|
||||
|
||||
--- Write a table as Lua code in curly brackets notation to a writer object.
|
||||
-- Only numbers, strings and tables (containing numbers, strings
|
||||
-- or other recursively processed tables) are supported.
|
||||
-- @param out table or userdata: a writer object supporting :write() method.
|
||||
-- @param tbl table: the table to be written.
|
||||
-- @param level number: the indentation level
|
||||
-- @param field_order table: optional prioritization table
|
||||
write_table = function(out, tbl, level, field_order)
|
||||
out:write("{")
|
||||
local sep = "\n"
|
||||
local indentation = " "
|
||||
local indent = true
|
||||
local i = 1
|
||||
for k, v, sub_order in util.sortedpairs(tbl, field_order) do
|
||||
out:write(sep)
|
||||
if indent then
|
||||
for n = 1,level do out:write(indentation) end
|
||||
end
|
||||
|
||||
if k == i then
|
||||
i = i + 1
|
||||
else
|
||||
if type(k) == "string" and k:match("^[a-zA-Z_][a-zA-Z0-9_]*$") then
|
||||
out:write(k)
|
||||
else
|
||||
out:write("[")
|
||||
write_value(out, k, level)
|
||||
out:write("]")
|
||||
end
|
||||
|
||||
out:write(" = ")
|
||||
end
|
||||
|
||||
write_value(out, v, level, sub_order)
|
||||
if type(v) == "number" then
|
||||
sep = ", "
|
||||
indent = false
|
||||
else
|
||||
sep = ",\n"
|
||||
indent = true
|
||||
end
|
||||
end
|
||||
if sep ~= "\n" then
|
||||
out:write("\n")
|
||||
for n = 1,level-1 do out:write(indentation) end
|
||||
end
|
||||
out:write("}")
|
||||
end
|
||||
|
||||
--- Write a table as series of assignments to a writer object.
|
||||
-- @param out table or userdata: a writer object supporting :write() method.
|
||||
-- @param tbl table: the table to be written.
|
||||
-- @param field_order table: optional prioritization table
|
||||
local function write_table_as_assignments(out, tbl, field_order)
|
||||
for k, v, sub_order in util.sortedpairs(tbl, field_order) do
|
||||
out:write(k.." = ")
|
||||
write_value(out, v, 0, sub_order)
|
||||
out:write("\n")
|
||||
end
|
||||
end
|
||||
|
||||
--- Save the contents of a table to a string.
|
||||
-- Each element of the table is saved as a global assignment.
|
||||
-- Only numbers, strings and tables (containing numbers, strings
|
||||
-- or other recursively processed tables) are supported.
|
||||
-- @param tbl table: the table containing the data to be written
|
||||
-- @param field_order table: an optional array indicating the order of top-level fields.
|
||||
-- @return string
|
||||
function persist.save_from_table_to_string(tbl, field_order)
|
||||
local out = {buffer = {}}
|
||||
function out:write(data) table.insert(self.buffer, data) end
|
||||
write_table_as_assignments(out, tbl, field_order)
|
||||
return table.concat(out.buffer)
|
||||
end
|
||||
|
||||
--- Save the contents of a table in a file.
|
||||
-- Each element of the table is saved as a global assignment.
|
||||
-- Only numbers, strings and tables (containing numbers, strings
|
||||
-- or other recursively processed tables) are supported.
|
||||
-- @param filename string: the output filename
|
||||
-- @param tbl table: the table containing the data to be written
|
||||
-- @param field_order table: an optional array indicating the order of top-level fields.
|
||||
-- @return boolean or (nil, string): true if successful, or nil and a
|
||||
-- message in case of errors.
|
||||
function persist.save_from_table(filename, tbl, field_order)
|
||||
local out = io.open(filename, "w")
|
||||
if not out then
|
||||
return nil, "Cannot create file at "..filename
|
||||
end
|
||||
write_table_as_assignments(out, tbl, field_order)
|
||||
out:close()
|
||||
return true
|
||||
end
|
||||
|
||||
return persist
|
||||
79
Utils/luarocks/lua/luarocks/purge.lua
Normal file
79
Utils/luarocks/lua/luarocks/purge.lua
Normal file
@ -0,0 +1,79 @@
|
||||
|
||||
--- Module implementing the LuaRocks "purge" command.
|
||||
-- Remove all rocks from a given tree.
|
||||
local purge = {}
|
||||
package.loaded["luarocks.purge"] = purge
|
||||
|
||||
local util = require("luarocks.util")
|
||||
local fs = require("luarocks.fs")
|
||||
local path = require("luarocks.path")
|
||||
local search = require("luarocks.search")
|
||||
local deps = require("luarocks.deps")
|
||||
local repos = require("luarocks.repos")
|
||||
local manif = require("luarocks.manif")
|
||||
local cfg = require("luarocks.cfg")
|
||||
local remove = require("luarocks.remove")
|
||||
|
||||
util.add_run_function(purge)
|
||||
purge.help_summary = "Remove all installed rocks from a tree."
|
||||
purge.help_arguments = "--tree=<tree> [--old-versions]"
|
||||
purge.help = [[
|
||||
This command removes rocks en masse from a given tree.
|
||||
By default, it removes all rocks from a tree.
|
||||
|
||||
The --tree argument is mandatory: luarocks purge does not
|
||||
assume a default tree.
|
||||
|
||||
--old-versions Keep the highest-numbered version of each
|
||||
rock and remove the other ones. By default
|
||||
it only removes old versions if they are
|
||||
not needed as dependencies. This can be
|
||||
overridden with the flag --force.
|
||||
]]
|
||||
|
||||
function purge.command(flags)
|
||||
local tree = flags["tree"]
|
||||
|
||||
if type(tree) ~= "string" then
|
||||
return nil, "The --tree argument is mandatory. "..util.see_help("purge")
|
||||
end
|
||||
|
||||
local results = {}
|
||||
local query = search.make_query("")
|
||||
query.exact_name = false
|
||||
if not fs.is_dir(tree) then
|
||||
return nil, "Directory not found: "..tree
|
||||
end
|
||||
|
||||
local ok, err = fs.check_command_permissions(flags)
|
||||
if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
|
||||
|
||||
search.manifest_search(results, path.rocks_dir(tree), query)
|
||||
|
||||
local sort = function(a,b) return deps.compare_versions(b,a) end
|
||||
if flags["old-versions"] then
|
||||
sort = deps.compare_versions
|
||||
end
|
||||
|
||||
for package, versions in util.sortedpairs(results) do
|
||||
for version, repositories in util.sortedpairs(versions, sort) do
|
||||
if flags["old-versions"] then
|
||||
util.printout("Keeping "..package.." "..version.."...")
|
||||
local ok, err = remove.remove_other_versions(package, version, flags["force"], flags["force-fast"])
|
||||
if not ok then
|
||||
util.printerr(err)
|
||||
end
|
||||
break
|
||||
else
|
||||
util.printout("Removing "..package.." "..version.."...")
|
||||
local ok, err = repos.delete_version(package, version, "none", true)
|
||||
if not ok then
|
||||
util.printerr(err)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return manif.make_manifest(cfg.rocks_dir, "one")
|
||||
end
|
||||
|
||||
return purge
|
||||
33
Utils/luarocks/lua/luarocks/refresh_cache.lua
Normal file
33
Utils/luarocks/lua/luarocks/refresh_cache.lua
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
--- Module implementing the luarocks-admin "refresh_cache" command.
|
||||
local refresh_cache = {}
|
||||
package.loaded["luarocks.refresh_cache"] = refresh_cache
|
||||
|
||||
local util = require("luarocks.util")
|
||||
local cfg = require("luarocks.cfg")
|
||||
local cache = require("luarocks.cache")
|
||||
|
||||
util.add_run_function(refresh_cache)
|
||||
refresh_cache.help_summary = "Refresh local cache of a remote rocks server."
|
||||
refresh_cache.help_arguments = "[--from=<server>]"
|
||||
refresh_cache.help = [[
|
||||
The flag --from indicates which server to use.
|
||||
If not given, the default server set in the upload_server variable
|
||||
from the configuration file is used instead.
|
||||
]]
|
||||
|
||||
function refresh_cache.command(flags)
|
||||
local server, upload_server = cache.get_upload_server(flags["server"])
|
||||
if not server then return nil, upload_server end
|
||||
local download_url = cache.get_server_urls(server, upload_server)
|
||||
|
||||
local ok, err = cache.refresh_local_cache(server, download_url, cfg.upload_user, cfg.upload_password)
|
||||
if not ok then
|
||||
return nil, err
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return refresh_cache
|
||||
174
Utils/luarocks/lua/luarocks/remove.lua
Normal file
174
Utils/luarocks/lua/luarocks/remove.lua
Normal file
@ -0,0 +1,174 @@
|
||||
|
||||
--- Module implementing the LuaRocks "remove" command.
|
||||
-- Uninstalls rocks.
|
||||
local remove = {}
|
||||
package.loaded["luarocks.remove"] = remove
|
||||
|
||||
local search = require("luarocks.search")
|
||||
local deps = require("luarocks.deps")
|
||||
local fetch = require("luarocks.fetch")
|
||||
local repos = require("luarocks.repos")
|
||||
local path = require("luarocks.path")
|
||||
local util = require("luarocks.util")
|
||||
local cfg = require("luarocks.cfg")
|
||||
local fs = require("luarocks.fs")
|
||||
local manif = require("luarocks.manif")
|
||||
|
||||
util.add_run_function(remove)
|
||||
remove.help_summary = "Uninstall a rock."
|
||||
remove.help_arguments = "[--force|--force-fast] <name> [<version>]"
|
||||
remove.help = [[
|
||||
Argument is the name of a rock to be uninstalled.
|
||||
If a version is not given, try to remove all versions at once.
|
||||
Will only perform the removal if it does not break dependencies.
|
||||
To override this check and force the removal, use --force.
|
||||
To perform a forced removal without reporting dependency issues,
|
||||
use --force-fast.
|
||||
|
||||
]]..util.deps_mode_help()
|
||||
|
||||
--- Obtain a list of packages that depend on the given set of packages
|
||||
-- (where all packages of the set are versions of one program).
|
||||
-- @param name string: the name of a program
|
||||
-- @param versions array of string: the versions to be deleted.
|
||||
-- @return array of string: an empty table if no packages depend on any
|
||||
-- of the given list, or an array of strings in "name/version" format.
|
||||
local function check_dependents(name, versions, deps_mode)
|
||||
local dependents = {}
|
||||
local blacklist = {}
|
||||
blacklist[name] = {}
|
||||
for version, _ in pairs(versions) do
|
||||
blacklist[name][version] = true
|
||||
end
|
||||
local local_rocks = {}
|
||||
local query_all = search.make_query("")
|
||||
query_all.exact_name = false
|
||||
search.manifest_search(local_rocks, cfg.rocks_dir, query_all)
|
||||
local_rocks[name] = nil
|
||||
for rock_name, rock_versions in pairs(local_rocks) do
|
||||
for rock_version, _ in pairs(rock_versions) do
|
||||
local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version))
|
||||
if rockspec then
|
||||
local _, missing = deps.match_deps(rockspec, blacklist, deps_mode)
|
||||
if missing[name] then
|
||||
table.insert(dependents, { name = rock_name, version = rock_version })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return dependents
|
||||
end
|
||||
|
||||
--- Delete given versions of a program.
|
||||
-- @param name string: the name of a program
|
||||
-- @param versions array of string: the versions to be deleted.
|
||||
-- @param deps_mode: string: Which trees to check dependencies for:
|
||||
-- "one" for the current default tree, "all" for all trees,
|
||||
-- "order" for all trees with priority >= the current default, "none" for no trees.
|
||||
-- @return boolean or (nil, string): true on success or nil and an error message.
|
||||
local function delete_versions(name, versions, deps_mode)
|
||||
|
||||
for version, _ in pairs(versions) do
|
||||
util.printout("Removing "..name.." "..version.."...")
|
||||
local ok, err = repos.delete_version(name, version, deps_mode)
|
||||
if not ok then return nil, err end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function remove.remove_search_results(results, name, deps_mode, force, fast)
|
||||
local versions = results[name]
|
||||
|
||||
local version = next(versions)
|
||||
local second = next(versions, version)
|
||||
|
||||
local dependents = {}
|
||||
if not fast then
|
||||
util.printout("Checking stability of dependencies in the absence of")
|
||||
util.printout(name.." "..table.concat(util.keys(versions), ", ").."...")
|
||||
util.printout()
|
||||
dependents = check_dependents(name, versions, deps_mode)
|
||||
end
|
||||
|
||||
if #dependents > 0 then
|
||||
if force or fast then
|
||||
util.printerr("The following packages may be broken by this forced removal:")
|
||||
for _, dependent in ipairs(dependents) do
|
||||
util.printerr(dependent.name.." "..dependent.version)
|
||||
end
|
||||
util.printerr()
|
||||
else
|
||||
if not second then
|
||||
util.printerr("Will not remove "..name.." "..version..".")
|
||||
util.printerr("Removing it would break dependencies for: ")
|
||||
else
|
||||
util.printerr("Will not remove installed versions of "..name..".")
|
||||
util.printerr("Removing them would break dependencies for: ")
|
||||
end
|
||||
for _, dependent in ipairs(dependents) do
|
||||
util.printerr(dependent.name.." "..dependent.version)
|
||||
end
|
||||
util.printerr()
|
||||
util.printerr("Use --force to force removal (warning: this may break modules).")
|
||||
return nil, "Failed removing."
|
||||
end
|
||||
end
|
||||
|
||||
local ok, err = delete_versions(name, versions, deps_mode)
|
||||
if not ok then return nil, err end
|
||||
|
||||
util.printout("Removal successful.")
|
||||
return true
|
||||
end
|
||||
|
||||
function remove.remove_other_versions(name, version, force, fast)
|
||||
local results = {}
|
||||
search.manifest_search(results, cfg.rocks_dir, { name = name, exact_name = true, constraints = {{ op = "~=", version = version}} })
|
||||
if results[name] then
|
||||
return remove.remove_search_results(results, name, cfg.deps_mode, force, fast)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Driver function for the "remove" command.
|
||||
-- @param name string: name of a rock. If a version is given, refer to
|
||||
-- a specific version; otherwise, try to remove all versions.
|
||||
-- @param version string: When passing a package name, a version number
|
||||
-- may also be given.
|
||||
-- @return boolean or (nil, string, exitcode): True if removal was
|
||||
-- successful, nil and an error message otherwise. exitcode is optionally returned.
|
||||
function remove.command(flags, name, version)
|
||||
if type(name) ~= "string" then
|
||||
return nil, "Argument missing. "..util.see_help("remove")
|
||||
end
|
||||
|
||||
local deps_mode = flags["deps-mode"] or cfg.deps_mode
|
||||
|
||||
local ok, err = fs.check_command_permissions(flags)
|
||||
if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end
|
||||
|
||||
local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$")
|
||||
local filename = name
|
||||
if rock_type then
|
||||
name, version = path.parse_name(filename)
|
||||
if not name then return nil, "Invalid "..rock_type.." filename: "..filename end
|
||||
end
|
||||
|
||||
local results = {}
|
||||
name = name:lower()
|
||||
search.manifest_search(results, cfg.rocks_dir, search.make_query(name, version))
|
||||
if not results[name] then
|
||||
return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..path.rocks_tree_to_string(cfg.root_dir)
|
||||
end
|
||||
|
||||
local ok, err = remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"])
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
manif.check_dependencies(nil, deps.get_deps_mode(flags))
|
||||
return true
|
||||
end
|
||||
|
||||
return remove
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user