Best controls

This commit is contained in:
Andros Fenollosa 2017-01-03 22:05:46 +01:00
parent 0bf3f6926e
commit 0182767625
257 changed files with 61 additions and 50819 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="love.to.android1208204140"
<manifest package="love.to.android1226120858"
android:versionCode="15"
android:versionName="0.9.2"
android:installLocation="auto" xmlns:android="http://schemas.android.com/apk/res/android">
@ -12,13 +12,13 @@
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="Game 1208204140"
android:label="Game 1226120858"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
<service android:name=".DownloadService" />
<activity
android:name="LtaActivity"
android:configChanges="orientation|screenSize"
android:label="Game 1208204140"
android:label="Game 1226120858"
android:launchMode="singleTop"
android:screenOrientation="landscape" >
<intent-filter>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="love.to.android1208204140"
<manifest package="love.to.android1226120858"
android:versionCode="15"
android:versionName="0.9.2"
android:installLocation="auto" xmlns:android="http://schemas.android.com/apk/res/android">
@ -12,13 +12,13 @@
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="Game 1208204140"
android:label="Game 1226120858"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
<service android:name=".DownloadService" />
<activity
android:name="LtaActivity"
android:configChanges="orientation|screenSize"
android:label="Game 1208204140"
android:label="Game 1226120858"
android:launchMode="singleTop"
android:screenOrientation="landscape" >
<intent-filter>

View File

@ -1,5 +1,5 @@
#Last build type
#Thu, 08 Dec 2016 20:41:46 +0100
#Mon, 26 Dec 2016 12:09:05 +0100
build.last.target=debug

View File

@ -1,9 +1,9 @@
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/classes.dex : \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/classes/love/to/android1208204140/BuildConfig.class \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/classes/love/to/android1208204140/LtaActivity.class \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/classes/love/to/android1208204140/R$attr.class \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/classes/love/to/android1208204140/R$drawable.class \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/classes/love/to/android1208204140/R.class \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/classes/love/to/android1226120858/BuildConfig.class \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/classes/love/to/android1226120858/LtaActivity.class \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/classes/love/to/android1226120858/R$attr.class \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/classes/love/to/android1226120858/R$drawable.class \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/classes/love/to/android1226120858/R.class \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/classes/org/libsdl/app/DummyEdit.class \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/classes/org/libsdl/app/SDLActivity$1.class \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/classes/org/libsdl/app/SDLActivity$2.class \

View File

@ -1,9 +1,9 @@
# view AndroidManifest.xml #generated:45
-keep class love.to.android1208204140.DownloadActivity { <init>(...); }
-keep class love.to.android1226120858.DownloadActivity { <init>(...); }
# view AndroidManifest.xml #generated:17
-keep class love.to.android1208204140.DownloadService { <init>(...); }
-keep class love.to.android1226120858.DownloadService { <init>(...); }
# view AndroidManifest.xml #generated:18
-keep class love.to.android1208204140.LtaActivity { <init>(...); }
-keep class love.to.android1226120858.LtaActivity { <init>(...); }

View File

@ -1,3 +1,3 @@
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/gen/love/to/android1208204140/R.java \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/gen/love/to/android1226120858/R.java \
: /Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/res/drawable-xxhdpi/ic_launcher.png \
/Users/androsfenollosa/www/alunizaje/android/tools/love-android-sdl2/bin/AndroidManifest.xml \

View File

@ -1,5 +1,5 @@
/** Automatically generated file. DO NOT MODIFY */
package love.to.android1208204140;
package love.to.android1226120858;
public final class BuildConfig {
public final static boolean DEBUG = true;

View File

@ -5,7 +5,7 @@
* should not be modified by hand.
*/
package love.to.android1208204140;
package love.to.android1226120858;
public final class R {
public static final class attr {

View File

@ -1,4 +1,4 @@
package love.to.android1208204140;
package love.to.android1226120858;
import org.love2d.android.GameActivity;
public class LtaActivity extends GameActivity {}

View File

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>ZeroBraneStudio</string>
<key>CFBundleDisplayName</key>
<string>ZeroBraneStudio</string>
<key>CFBundleGetInfoString</key>
<string></string>
<key>CFBundleIconFile</key>
<string>zbstudio.icns</string>
<key>CFBundleIdentifier</key>
<string>com.ZeroBrane.ZeroBraneStudio</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string></string>
<key>CFBundleName</key>
<string>ZeroBraneStudio</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string></string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string></string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSRequiresCarbon</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string></string>
</dict>
</plist>

View File

@ -1,9 +0,0 @@
#!/bin/bash
ZBS_PATH=${0%/*/*}
if [ ! -d $ZBS_PATH ]; then ZBS_PATH=${PWD%/*}; fi
ZBS_BIN="$ZBS_PATH/ZeroBraneStudio/bin"
if [[ ! -e "$ZBS_BIN/libedit.3.dylib" && ! -e /usr/lib/libedit.3.dylib ]]
then ln -s /usr/lib/libedit.2.dylib "$ZBS_BIN/libedit.3.dylib"; fi
export DYLD_LIBRARY_PATH="$ZBS_BIN:${DYLD_LIBRARY_PATH}"
(cd "$ZBS_PATH/ZeroBraneStudio"; bin/lua src/main.lua zbstudio "$@")

View File

@ -1,237 +0,0 @@
--[[ ZeroBrane Studio License ]]-------------------------------------------
ZeroBrane Studio sources are released under the MIT License
Copyright (c) 2011-2015 Paul Kulchenko (paul@kulchenko.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
--[[ Estrela Editor License ]]---------------------------------------------
Estrela Editor sources are released under the MIT License
Copyright (c) 2008-2012
Luxinia DevTeam:
Christoph Kubisch & Eike Decker
info at luxinia.de
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
--[[ wxLua License ]]------------------------------------------------------
http://wxlua.sourceforge.net/
Pre-Built binaries for wxLua/WxWindows
Original wxLua Lua sample IDE:
Lomtick Software
J. Winwood & John Labenski
luascript at thersgb.net
wxLua is based on
wxWindows Library License, Version 3
Copyright (c) 1998 Julian Smart, Robert Roebling et al
Everyone is permitted to copy and distribute verbatim copies
of this licence document, but changing it is not allowed.
WXWINDOWS LIBRARY LICENCE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public Licence as published by
the Free Software Foundation; either version 2 of the Licence, or (at
your option) any later version.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library
General Public Licence for more details.
You should have received a copy of the GNU Library General Public Licence
along with this software, usually in a file named COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA.
EXCEPTION NOTICE
1. As a special exception, the copyright holders of this library give
permission for additional uses of the text contained in this release of
the library as licenced under the wxWindows Library Licence, applying
either version 3 of the Licence, or (at your option) any later version of
the Licence as published by the copyright holders of version 3 of the
Licence document.
2. The exception is that you may use, copy, link, modify and distribute
under the user's own terms, binary object code versions of works based
on the Library.
3. If you copy code from files distributed under the terms of the GNU
General Public Licence or the GNU Library General Public Licence into a
copy of this library, as this licence permits, the exception does not
apply to the code that you add in this way. To avoid misleading anyone as
to the status of such modified files, you must delete this exception
notice from such code and/or adjust the licensing conditions notice
accordingly.
4. If you write modifications of your own for this library, it is your
choice whether to permit this exception to apply to your modifications.
If you do not wish that, you must delete the exception notice from such
code and/or adjust the licensing conditions notice accordingly.
--[[ Lua License ]]--------------------------------------------------------
Copyright: © 1994-2006 Lua.org, PUC-Rio.
Homepage: http://www.lua.org
License: http://www.lua.org/copyright.html
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
--[[ LuaSockets License ]]-------------------------------------------------
Copyright: © 2004-2006 Diego Nehab. All rights reserved.
Homepage: http://www.cs.princeton.edu/~diego/professional/luasocket/
License: http://www.lua.org/copyright.html (same as LUA)
--[[ Serpent License ]]----------------------------------------------------
Copyright (c) 2011-2013 Paul Kulchenko (paul@kulchenko.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
--[[ LuaJIT License ]]-----------------------------------------------------
Copyright © 2005-2013 Mike Pall, released under the MIT open source license.
--[[ WinAPI License ]]-----------------------------------------------------
Copyright (C) 2011 Steve Donovan.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
--[[ MobDebug License ]]---------------------------------------------------
MobDebug sources are released under the MIT License
Copyright (c) 2011-2012 Paul Kulchenko (paul@kulchenko.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
--[[ RemDebug License ]]---------------------------------------------------
Copyright (c) 2006 The Kepler Project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,113 +0,0 @@
# Project Description
[ZeroBrane Studio](http://studio.zerobrane.com/) is a lightweight cross-platform Lua IDE with code completion,
syntax highlighting, remote debugger, code analyzer, live coding,
and debugging support for several Lua engines
([Lua 5.1](http://studio.zerobrane.com/doc-lua-debugging),
[Lua 5.2](http://studio.zerobrane.com/doc-lua52-debugging),
[Lua 5.3](http://studio.zerobrane.com/doc-lua53-debugging),
[LuaJIT](http://studio.zerobrane.com/doc-luajit-debugging),
[LÖVE](http://notebook.kulchenko.com/zerobrane/love2d-debugging),
[Moai](http://notebook.kulchenko.com/zerobrane/moai-debugging-with-zerobrane-studio),
[Gideros](http://notebook.kulchenko.com/zerobrane/gideros-debugging-with-zerobrane-studio-ide),
[Corona](http://notebook.kulchenko.com/zerobrane/debugging-and-live-coding-with-corona-sdk-applications-and-zerobrane-studio),
[Marmalade Quick](http://notebook.kulchenko.com/zerobrane/marmalade-quick-debugging-with-zerobrane-studio),
[Cocos2d-x](http://notebook.kulchenko.com/zerobrane/cocos2d-x-simulator-and-on-device-debugging-with-zerobrane-studio),
[OpenResty/Nginx](http://notebook.kulchenko.com/zerobrane/debugging-openresty-nginx-lua-scripts-with-zerobrane-studio),
[Torch7](http://notebook.kulchenko.com/zerobrane/torch-debugging-with-zerobrane-studio),
[Redis](http://notebook.kulchenko.com/zerobrane/redis-lua-debugging-with-zerobrane-studio),
[GSL-shell](http://notebook.kulchenko.com/zerobrane/gsl-shell-debugging-with-zerobrane-studio),
[Adobe Lightroom](http://notebook.kulchenko.com/zerobrane/debugging-lightroom-plugins-zerobrane-studio-ide),
[Lapis](http://notebook.kulchenko.com/zerobrane/lapis-debugging-with-zerobrane-studio),
[Moonscript](http://notebook.kulchenko.com/zerobrane/moonscript-debugging-with-zerobrane-studio),
and others). It originated from the [Estrela Editor](http://www.luxinia.de/index.php/Estrela/).
![ZeroBrane Studio debugger screenshot](http://studio.zerobrane.com/images/debugging.png)
## Features
* Written in Lua, so easily customizable.
* Small, portable, and cross-platform (Windows, Mac OSX, and Linux).
* Auto-completion for functions, keywords, and custom APIs.
* Interactive console to directly test code snippets with local and remote execution.
* Integrated debugger with local and [remote debugging](http://studio.zerobrane.com/doc-remote-debugging)
for [Lua 5.1](http://studio.zerobrane.com/doc-lua-debugging),
[Lua 5.2](http://studio.zerobrane.com/doc-lua52-debugging),
[Lua 5.3](http://studio.zerobrane.com/doc-lua53-debugging),
[LuaJIT](http://studio.zerobrane.com/doc-luajit-debugging),
and [other Lua engines](http://studio.zerobrane.com/documentation#debugging).
* [Live coding](http://studio.zerobrane.com/documentation#live_coding)
with [Lua](http://notebook.kulchenko.com/zerobrane/live-coding-in-lua-bret-victor-style),
[LÖVE](http://notebook.kulchenko.com/zerobrane/live-coding-with-love),
[Gideros](http://notebook.kulchenko.com/zerobrane/gideros-live-coding-with-zerobrane-studio-ide),
[Moai](http://notebook.kulchenko.com/zerobrane/live-coding-with-moai-and-zerobrane-studio),
[Corona SDK](http://notebook.kulchenko.com/zerobrane/debugging-and-live-coding-with-corona-sdk-applications-and-zerobrane-studio),
GSL-shell, and other engines.
* Function outline.
* Fuzzy search with `Go To File`, project-wide `Go To Symbol`, and `Insert Library Function`.
* Several ways to extend the current functionality:
- specs (`spec/`): specifications for file syntax, lexer, and keywords;
- apis (`api/`): descriptions for [code completion and tooltips](http://studio.zerobrane.com/doc-api-auto-complete);
- interpreters (`interpreters/`): components for setting debugging and run-time project environment;
- packages (`packages/`): [plugins](http://studio.zerobrane.com/doc-plugin) that provide additional functionality;
- config (`cfg/`): settings for styles, color themes, and other preferences;
- translations (`cfg/i18n/`): [translations](http://studio.zerobrane.com/doc-translation) of the menus and messages to other languages;
- tools (`tools/`): additional tools.
## Documentation
* A [short and simple overview](http://studio.zerobrane.com/doc-getting-started) for those who are new to this development environment.
* A list of [frequently asked questions](http://studio.zerobrane.com/doc-faq) about the IDE.
* [Tutorials and demos](http://studio.zerobrane.com/tutorials) that cover debugging and live coding for different environments.
* [Tips and tricks](http://studio.zerobrane.com/doc-tips-and-tricks).
## Installation
ZeroBrane Studio can be installed into and run from any folder.
No compilation is needed, although the scripts to compile required libraries for Windows, OSX, and Linux platforms are available in the `build/` folder.
## Usage
```
Open file(s):
zbstudio [option] [<project directory>] <filename> [<filename>...]
non-options are treated as a project directory to set or a file to open
Set project directory:
zbstudio <project directory> [<filename>...]
(0.39+) a directory passed as a parameter will be set as the project directory
Override default configuration:
zbstudio -cfg "<luacode overriding config>" [<filename>]
e.g.: zbstudio -cfg "editor.fontsize=12" somefile.lua
Load custom configuration:
zbstudio -cfg path/file.lua [<filename>]
e.g.: zbstudio -cfg cfg/estrela.lua
```
If you are loading a file, you can also request the cursor to be set on a particular line or at a particular position by using `filename:<line>` and `filename:p<pos>` syntax (0.71+).
## Contributing
See [CONTRIBUTING](CONTRIBUTING.md).
## Author
### ZeroBrane Studio and MobDebug
**ZeroBrane LLC:** Paul Kulchenko (paul@kulchenko.com)
### Estrela Editor
**Luxinia Dev:** Christoph Kubisch (crazybutcher@luxinia.de)
## Where is Estrela?
The Estrela project has been merged into ZeroBrane Studio. If you have used Estrela
for graphics shader authoring or luxinia, create/modify the `cfg/user.lua` and
add `include "estrela"` (1.21+) to load all tools and specifications by default again.
## License
See [LICENSE](LICENSE).

View File

@ -1,205 +0,0 @@
-- authors: Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
local function fn (description)
local description2,returns,args = description:match("(.+)%-%s*(%b())%s*(%b())")
if not description2 then
return {type="function",description=description,
returns="(?)"}
end
return {type="function",description=description2,
returns=returns:gsub("^%s+",""):gsub("%s+$",""), args = args}
end
local function val (description)
return {type="value",description = description}
end
-- docs
local api = {
abs = fn "returns absolute value of scalars and vectors. - (typeN)(typeN)",
acos = fn "returns arccosine of scalars and vectors. - (typeN)(typeN)",
all = fn "returns true if a boolean scalar or all components of a boolean vector are true. - (bool)(boolN)",
any = fn "returns true if a boolean scalar or any component of a boolean vector is true. - (bool)(boolN)",
asin = fn "returns arcsine of scalars and vectors. - (typeN)(typeN)",
atan = fn "returns arctangent of scalars and vectors. - (typeN)(typeN)",
atan2 = fn "returns the arctangent of y/x. atan2 is well defined for every point other than the origin, even if x equals 0 and y does not equal 0. - (typeN)(typeN y, typeN x)",
ceil = fn "returns smallest integer not less than a scalar or each vector component. - (typeN)(typeN)",
clamp = fn "returns x clamped to the range [a,b]. - (typeN)(typeN x, a, b)",
clip = fn "conditionally (<0) kill a pixel before output. - ()(typeN)",
cos = fn "returns cosine of scalars and vectors. - (typeN)(typeN)",
cosh = fn "returns hyperbolic cosine of scalars and vectors. - (typeN)(typeN)",
cross = fn "returns the cross product of two three-component vectors. - (type3)(type3 a, b)",
ddx = fn "returns approximate partial derivative with respect to window-space X. - (typeN)(typeN)",
ddy = fn "returns approximate partial derivative with respect to window-space Y. - (typeN)(typeN)",
degrees = fn "converts values of scalars and vectors from radians to degrees. - (typeN)(typeN)",
determinant = fn "returns the scalar determinant of a square matrix. - (float)(floatNxN)",
distance = fn "return the Euclidean distance between two points. - (typeN)(typeN a, b)",
dot = fn "returns the scalar dot product of two vectors. - (type)(typeN a, b)",
exp = fn "returns the base-e exponential of scalars and vectors. - (typeN)(typeN)",
exp2 = fn "returns the base-2 exponential of scalars and vectors. - (typeN)(typeN)",
faceforward = fn "returns a normal as-is if a vertex's eye-space position vector points in the opposite direction of a geometric normal, otherwise return the negated version of the normal. - (typeN)(typeN Nperturbated, Incident, Ngeometric)",
floatToIntBits = fn "returns the 32-bit integer representation of an IEEE 754 floating-point scalar or vector - (intN)(floatN)",
floatToRawIntBits = fn "returns the raw 32-bit integer representation of an IEEE 754 floating-point scalar or vector. - (intN)(floatN)",
floor = fn "returns largest integer not greater than a scalar or each vector component. - (typeN)(typeN)",
fmod = fn "returns the remainder of x/y with the same sign as x. - (typeN)(typeN x, y)",
frac = fn "returns the fractional portion of a scalar or each vector component. - (typeN)(typeN)",
frexp = fn "splits scalars and vectors into normalized fraction and a power of 2. - (typeN)(typeN x, out typeN e)",
fwidth = fn "returns sum of approximate window-space partial derivatives magnitudes. - (typeN)(typeN)",
intBitsToFloat = fn "returns the float value corresponding to a given bit represention.of a scalar int value or vector of int values. - (floatN)(intN)",
isfinite = fn "test whether or not a scalar or each vector component is a finite value. - (boolN)(typeN)",
isinf = fn "test whether or not a scalar or each vector component is infinite. - (boolN)(typeN)",
isnan = fn "test whether or not a scalar or each vector component is not-a-number. - (boolN)(typeN)",
ldexp = fn "returns x times 2 rained to n. - (typeN)(typeN a, n)",
length = fn "return scalar Euclidean length of a vector. - (type)(typeN)",
lerp = fn "lerp - returns linear interpolation of two scalars or vectors based on a weight. - (typeN)(typeN a, b, weight)",
lit = fn "computes lighting coefficients for ambient(x), diffuse(y), and specular(z) lighting contributions (w=1). - (type4)(type NdotL, NdotH, specshiny)",
log = fn "returns the natural logarithm of scalars and vectors. - (typeN)(typeN)",
log10 = fn "returns the base-10 logarithm of scalars and vectors. - (typeN)(typeN)",
log2 = fn "returns the base-2 logarithm of scalars and vectors. - (typeN)(typeN)",
max = fn "returns the maximum of two scalars or each respective component of two vectors. - (typeN)(typeN a, b)",
min = fn "returns the minimum of two scalars or each respective component of two vectors. - (typeN)(typeN a, b)",
mul = fn "Returns the vector result of multiplying a matrix M by a column vector v; a row vector v by a matrix M; or a matrix A by a second matrix B. - (typeN)(typeNxN/typeN a, typeN/typeNxN b)",
normalize = fn "Returns the normalized version of a vector, meaning a vector in the same direction as the original vector but with a Euclidean length of one. - (typeN)(typeN)",
pow = fn "returns x to the y-th power of scalars and vectors. - (typeN)(typeN x, y)",
radians = fn "converts values of scalars and vectors from degrees to radians. - (typeN)(typeN)",
reflect = fn "returns the reflectiton vector given an incidence vector and a normal vector. - (typeN)(typeN incidence, normal)",
refract = fn "computes a refraction vector. - (typeN)(typeN incidence, normal, type eta)",
round = fn "returns the rounded value of scalars or vectors. - (typeN)(typeN a)",
rsqrt = fn "returns reciprocal square root of scalars and vectors. 1/sqrt. - (typeN)(typeN)",
saturate = fn "returns x saturated to the range [0,1]. - (typeN)(typeN)",
sign = fn "returns sign (1 or -1) of scalar or each vector component. - (typeN)(typeN)",
sin = fn "returns sine of scalars and vectors. - (typeN)(typeN)",
sincos = fn "returns sine of scalars and vectors. - ()(typeN x, out typeN sin, out typeN cos)",
sinh = fn "returns hyperbolic sine of scalars and vectors. - (typeN)(typeN)",
sqrt = fn "returns square root of scalars and vectors. - (typeN)(typeN)",
step = fn "implement a step function returning either zero or one (a <= b). - (typeN)(typeN a, b)",
tan = fn "returns tangent of scalars and vectors. - (typeN)(typeN)",
tanh = fn "returns hyperbolic tangent of scalars and vectors. - (typeN)(typeN)",
transpose = fn "returns transpose matrix of a matrix. - (typeRxC)(typeCxR)",
trunc = fn "returns largest integer not greater than a scalar or each vector component. - (typeN)(typeN)",
tex1D = fn "performs a texture lookup in a given 1D sampler and, in some cases, a shadow comparison (as .y coord). May also use pre computed derivatives if those are provided. Texeloffset only in gp4 or higher profiles. - (float4)(sampler1D, float/float2 s, |float dx, dy|,[int texeloffset])",
tex1Dbias = fn "performs a texture lookup with bias in a given sampler (as .w). - (float4)(sampler1D, float4 s, [int texeloffset])",
tex1Dcmpbias = fn "performs a texture lookup with bias and shadow compare in a given sampler (compare as .y, bias as .w). - (float4)(sampler1D, float4 s, [int texeloffset])",
tex1Dcmplod = fn "performs a texture lookup with a specified level of detail and a shadow compare in a given sampler (compare as .y, lod as .w). - (float4)(sampler1D, float4 s, [int texeloffset])",
tex1Dfetch = fn "performs an unfiltered texture lookup in a given sampler (lod as .w). - (float4)(sampler1D, int4 s, [int texeloffset])",
tex1Dlod = fn "performs a texture lookup with a specified level of detail in a given sampler (lod as .w) - (float4)(sampler1D, float4 s, [int texeloffset])",
tex1Dproj = fn "performs a texture lookup with projection in a given sampler. May perform a shadow comparison if argument for shadow comparison is provided. (shadow in .y for float3 coord, proj in .y or .z) - (float4)(sampler1D, float2/float3 s, [int texeloff])",
tex1Dsize = fn "returns the size of a given texture image for a given level of detail. (only gp4 profiles) - (int3)(sampler1D, int lod)",
tex2D = fn "performs a texture lookup in a given 2D sampler and, in some cases, a shadow comparison (as .z coord). May also use pre computed derivatives if those are provided. Texeloffset only in gp4 or higher profiles. - (float4)(sampler2D, float2/float3 s, |float2 dx, dy|,[int texeloffset])",
tex2Dbias = fn "performs a texture lookup with bias in a given sampler (as .w). - (float4)(sampler2D, float4 s, [int texeloffset])",
tex2Dcmpbias = fn "performs a texture lookup with bias and shadow compare in a given sampler (compare as .z, bias as .w). - (float4)(sampler2D, float4 s, [int texeloffset])",
tex2Dcmplod = fn "performs a texture lookup with a specified level of detail and a shadow compare in a given sampler (compare as .y, lod as .w). - (float4)(sampler2D, float4 s, [int texeloffset])",
tex2Dfetch = fn "performs an unfiltered texture lookup in a given sampler (lod as .w). - (float4)(sampler2D, int4 s, [int texeloffset])",
tex2Dlod = fn "performs a texture lookup with a specified level of detail in a given sampler (lod as .w) - (float4)(sampler2D, float4 s, [int texeloffset])",
tex2Dproj = fn "performs a texture lookup with projection in a given sampler. May perform a shadow comparison if argument for shadow comparison is provided. (shadow in .z for float3 coord, proj in .z or .w) - (float4)(sampler2D, float3/float4 s, [int texeloff])",
tex2Dsize = fn "returns the size of a given texture image for a given level of detail. (only gp4 profiles) - (int3)(sampler2D, int lod)",
tex2Dgather = fn "returns 4 texels of a given single channel texture image for a given level of detail. (only gp4 profiles) - (int3)(sampler2D, int lod)",
tex3D = fn "performs a texture lookup in a given 3D sampler. May also use pre computed derivatives if those are provided. Texeloffset only in gp4 or higher profiles. - (float4)(sampler3D, float3 s, {float3 dx, dy},[int texeloffset])",
tex3Dbias = fn "performs a texture lookup with bias in a given sampler (as .w). - (float4)(sampler3D, float4 s, [int texeloffset])",
tex3Dfetch = fn "performs an unfiltered texture lookup in a given sampler (lod as .w). - (float4)(sampler3D, int4 s, [int texeloffset])",
tex3Dlod = fn "performs a texture lookup with a specified level of detail in a given sampler (lod as .w) - (float4)(sampler3D, float4 s, [int texeloffset])",
tex3Dproj = fn "performs a texture lookup with projection in a given sampler. (proj in .w) - (float4)(sampler3D, float4 s, [int texeloff])",
tex3Dsize = fn "returns the size of a given texture image for a given level of detail. (only gp4 profiles) - (int3)(sampler3D, int lod)",
texBUF = fn "performs an unfiltered texture lookup in a given texture buffer sampler. (only gp4 profiles) - (float4)(samplerBUF, int s)",
texBUFsize = fn "returns the size of a given texture image for a given level of detail. (only gp4 profiles) - (int3)(samplerBUF, int lod)",
texRBUF = fn "performs a multi-sampled texture lookup in a renderbuffer. (only gp4 profiles) - (float4)(samplerRBUF, int2 s, int sample)",
texRBUFsize = fn "returns the size of a given renderbuffer. (only gp4 profiles) - (int2)(samplerBUF)",
texCUBE = fn "performs a texture lookup in a given CUBE sampler and, in some cases, a shadow comparison (float4 coord). May also use pre computed derivatives if those are provided. Texeloffset only in gp4 or higher profiles. - (float4)(samplerCUBE, float3/float4 s, |float3 dx, dy|)",
texCUBEbias = fn "performs a texture lookup with bias in a given sampler (as .w). - (float4)(sampler1D, float4 s, [int texeloffset])",
texCUBElod = fn "performs a texture lookup with a specified level of detail in a given sampler (lod as .w) - (float4)(sampler1D, float4 s, [int texeloffset])",
texCUBEproj = fn "performs a texture lookup with projection in a given sampler. (proj in .w) - (float4)(samplerCUBE, float4 s)",
texCUBEsize = fn "returns the size of a given texture image for a given level of detail. (only gp4 profiles) - (int3)(sampler1D, int lod)",
texRECT = fn "performs a texture lookup in a given RECT sampler and, in some cases, a shadow comparison (as .z). May also use pre computed derivatives if those are provided. Texeloffset only in gp4 or higher profiles. - (float4)(samplerRECT, float2/float3 s, |float2 dx, dy|, [int texeloff])",
texRECTbias = fn "performs a texture lookup with bias in a given sampler (as .w). - (float4)(samplerRECT, float4 s, [int texeloffset])",
texRECTfetch = fn "performs an unfiltered texture lookup in a given sampler (lod as .w). - (float4)(samplerRECT, int4 s, [int texeloffset])",
texRECTlod = fn "performs a texture lookup with a specified level of detail in a given sampler (lod as .w) - (float4)(samplerRECT, float4 s, [int texeloffset])",
texRECTproj = fn "performs a texture lookup with projection in a given sampler. May perform a shadow comparison if argument for shadow comparison is provided. (shadow in .z for float3 coord, proj in .z or .w) - (float4)(samplerRECT, float3/float4 s, [int texeloff])",
texRECTsize = fn "returns the size of a given texture image for a given level of detail. (only gp4 profiles) - (int3)(samplerRECT, int lod)",
tex1DARRAY = fn "performs a texture lookup in a given 1D sampler array and, in some cases, a shadow comparison (as .z). May also use pre computed derivatives if those are provided. Texeloffset only in gp4 or higher profiles. - (float4)(sampler1DARRAY, float2/float3 s, {float dx, dy},[int texeloffset])",
tex1DARRAYbias = fn "performs a texture lookup with bias in a given sampler (as .w). - (float4)(sampler1DARRAY, float4 s, [int texeloffset])",
tex1DARRAYcmpbias = fn "performs a texture lookup with bias and shadow compare in a given sampler (layer as .y, compare as .z, bias as .w). - (float4)(sampler1DARRAY, float4 s, [int texeloffset])",
tex1DARRAYcmplod = fn "performs a texture lookup with a specified level of detail and a shadow compare in a given sampler (compare as .z, lod as .w). - (float4)(sampler1DARRAY, float4 s, [int texeloffset])",
tex1DARRAYfetch = fn "performs an unfiltered texture lookup in a given sampler (lod as .z). - (float4)(sampler1DARRAY, int3 s, [int texeloffset])",
tex1DARRAYlod = fn "performs a texture lookup with a specified level of detail in a given sampler (lod as .z) - (float4)(sampler1DARRAY, float3 s, [int texeloffset])",
tex1DARRAYproj = fn "performs a texture lookup with projection in a given sampler. May perform a shadow comparison if argument for shadow comparison is provided. (shadow in .z for float3 coord, proj in .z or .w) - (float4)(sampler1DARRAY, float3/float4 s, [int texeloff])",
tex1DARRAYsize = fn "returns the size of a given texture image for a given level of detail. (only gp4 profiles) - (int3)(sampler1DARRAY, int lod)",
tex2DARRAY = fn "performs a texture lookup in a given 2D sampler array and, in some cases, a shadow comparison (as .w coord). May also use pre computed derivatives if those are provided. Texeloffset only in gp4 or higher profiles. - (float4)(sampler2DARRAY, float3/float4 s, {float2 dx, dy},[int texeloffset])",
tex2DARRAYbias = fn "performs a texture lookup with bias in a given sampler (as .w). - (float4)(sampler2DARRAY, float4 s, [int texeloffset])",
tex2DARRAYfetch = fn "performs an unfiltered texture lookup in a given sampler (lod as .w). - (float4)(sampler2DARRAY, int4 s, [int texeloffset])",
tex2DARRAYlod = fn "performs a texture lookup with a specified level of detail in a given sampler (lod as .w) - (float4)(sampler2DARRAY, float4 s, [int texeloffset])",
tex2DARRAYproj = fn "performs a texture lookup with projection in a given sampler. May perform a shadow comparison if argument for shadow comparison is provided. (proj in .w) - (float4)(sampler2DARRAY, float4 s, [int texeloff])",
tex2DARRAYsize = fn "returns the size of a given texture image for a given level of detail. (only gp4 profiles) - (int3)(sampler2DARRAY, int lod)",
texCUBEARRAY = fn "performs a texture lookup in a given CUBE sampler array. May also use pre computed derivatives if those are provided. Texeloffset only in gp4 or higher profiles. - (float4)(samplerCUBEARRAY, float4 s, {float3 dx, dy},[int texeloffset])",
texCUBEARRAYsize = fn "returns the size of a given texture image for a given level of detail. (only gp4 profiles) - (int3)(samplerCUBEARRAY, int lod)",
unpack_4ubyte = fn "interprets the single float as 4 normalized unsigned bytes and returns the vector. (only nv/gp4 profiles) - (float4)(float)",
pack_4ubyte = fn "packs the floats into a single storing as normalized unsigned bytes.(only nv/gp4 profiles) - (float)(float4)",
unpack_4byte = fn "interprets the single float as 4 normalized signed bytes and returns the vector. (only nv/gp4 profiles) - (float4)(float)",
pack_4ubyte = fn "packs the floats into a single storing as normalized signed bytes.(only nv/gp4 profiles) - (float)(float4)",
unpack_4ushort = fn "interprets the single float as 2 normalized unsigned shorts and returns the vector. (only nv/gp4 profiles) - (float2)(float)",
pack_4ushort = fn "packs the floats into a single storing as normalized unsigned shorts.(only nv/gp4 profiles) - (float)(float2)",
unpack_2half = fn "interprets the single float as 2 16-bit floats and returns the vector. (only nv/gp4 profiles) - (float2)(float)",
pack_2half = fn "packs the floats into a single storing as 16-bit floats.(only nv/gp4 profiles) - (float)(float2)",
}
local keyw =
[[int half float float3 float4 float2 float3x3 float3x4 float4x3 float4x4
float1x2 float2x1 float2x2 float2x3 float3x2 float1x3 float3x1 float4x1 float1x4
float2x4 float4x2 double1x4 double4x4 double4x2 double4x3 double3x4 double2x4 double1x4
double half half2 half3 half4 int2 int3 uint uint2 uint3 uint4
int4 bool bool2 bool3 bool4 string struct typedef
usampler usampler1D usampler2D usampler3D usamplerRECT usamplerCUBE isampler1DARRAY usampler2DARRAY usamplerCUBEARRAY
isampler isampler1D isampler2D isampler3D isamplerRECT isamplerCUBE isampler1DARRAY isampler2DARRAY isamplerCUBEARRAY
usamplerBUF isamplerBUF samplerBUF
sampler sampler1D sampler2D sampler3D samplerRECT samplerCUBE sampler1DARRAY sampler2DARRAY samplerCUBEARRAY
texture texture1D texture2D texture3D textureRECT textureCUBE texture1DARRAY texture2DARRAY textureCUBEARRAY
decl do else extern false for if in inline inout out pass
pixelshader return shared static string technique true
uniform vector vertexshader void volatile while
asm compile const auto break case catch char class const_cast continue default delete
dynamic_cast enum explicit friend goto long mutable namespace new operator private protected
public register reinterpret_case short signed sizeof static_cast switch template this throw
try typename union unsigned using virtual
POSITION PSIZE DIFFUSE SPECULAR TEXCOORD FOG COLOR COLOR0 COLOR1 COLOR2 COLOR3 TEXCOORD0 TEXCOORD1 TEXCOORD2 TEXCOORD3
TEXCOORD4 TEXCOORD5 TEXCOORD6 TEXCOORD7 TEXCOORD8 TEXCOORD9 TEXCOORD10 TEXCOORD11 TEXCOORD12 TEXCOORD13 TEXCOORD14
TEXCOORD15
NORMAL WPOS
ATTR0 ATTR1 ATTR2 ATTR3 ATTR4 ATTR5 ATTR6 ATTR7 ATTR8 ATTR9 ATTR10 ATTR11 ATTR12 ATTR13 ATTR14 ATTR15
TEXUNIT0 TEXUNIT1 TEXUNIT2 TEXUNIT3 TEXUNIT4 TEXUNIT5 TEXUNIT6 TEXUNIT7 TEXUNIT8 TEXUNIT9 TEXUNIT10 TEXUNIT11 TEXUNIT12
TEXUNIT13 TEXUNIT14 TEXUNIT15
PROJ PROJECTION PROJECTIONMATRIX PROJMATRIX
PROJMATRIXINV PROJINV PROJECTIONINV PROJINVERSE PROJECTIONINVERSE PROJINVMATRIX PROJECTIONINVMATRIX PROJINVERSEMATRIX PROJECTIONINVERSEMATRIX
VIEW VIEWMATRIX VIEWMATRIXINV VIEWINV VIEWINVERSE VIEWINVERSEMATRIX VIEWINVMATRIX
VIEWPROJECTION VIEWPROJ VIEWPROJMATRIX VIEWPROJECTIONMATRIX
WORLD WORLDMATRIX WORLDVIEW WORLDVIEWMATRIX
WORLDVIEWPROJ WORLDVIEWPROJECTION WORLDVIEWPROJMATRIX WORLDVIEWPROJECTIONMATRIX
VIEWPORTSIZE VIEWPORTDIMENSION
VIEWPORTSIZEINV VIEWPORTSIZEINVERSE VIEWPORTDIMENSIONINV VIEWPORTDIMENSIONINVERSE INVERSEVIEWPORTDIMENSIONS
FOGCOLOR FOGDISTANCE CAMERAWORLDPOS CAMERAWORLDDIR
CENTROID FLAT NOPERSPECTIVE FACE PRIMITIVEID VERTEXID
]]
-- keywords - shouldn't be left out
for w in keyw:gmatch("([_%w]+)") do
api[w] = {type="keyword"}
end
return api

View File

@ -1,277 +0,0 @@
-- authors: Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
-- function helpers
local function fn (description)
local description2,returns,args = description:match("(.+)%-%s*(%b())%s*(%b())")
if not description2 then
return {type="function",description=description,
returns="(?)"}
end
return {type="function",description=description2,
returns=returns:gsub("^%s+",""):gsub("%s+$",""), args = args}
end
local function val (description)
return {type="value",description = description}
end
-- docs
local api = {
radians = fn "converts degrees to radians - (vecN)(vecN)",
degrees = fn "converts radians to degrees - (vecN)(vecN)",
sin = fn "returns sine of scalars and vectors. - (vecN)(vecN)",
sinh = fn "returns hyperbolic sine of scalars and vectors. - (vecN)(vecN)",
cos = fn "returns cosine of scalars and vectors. - (vecN)(vecN)",
cosh = fn "returns hyperbolic cosine of scalars and vectors. - (vecN)(vecN)",
atan = fn "returns arc tangent of scalars and vectors. - (vecN)([vecN y_over_x ]/[vecN y, vecN x])",
asin = fn "returns arc sine of scalars and vectors. - (vecN)(vecN)",
acos = fn "returns arc cosine of scalars and vectors. - (vecN)(vecN)",
atan = fn "returns arc tangent of scalars and vectors. - (vecN)(vecN)",
tan = fn "returns tangent of scalars and vectors. - (vecN)(vecN)",
tanh = fn "returns hyperbolic tangent of scalars and vectors. - (vecN)(vecN)",
acosh = fn "returns hyperbolic arc cosine of scalars and vectors. - (vecN)(vecN)",
asinh = fn "returns hyperbolic arc sine of scalars and vectors. - (vecN)(vecN)",
atanh = fn "returns hyperbolic arc tangent of scalars and vectors. - (vecN)(vecN)",
exp = fn "returns the base-e exponential of scalars and vectors. - (vecN)(vecN)",
exp2 = fn "returns the base-2 exponential of scalars and vectors. - (vecN)(vecN)",
log = fn "returns the natural logarithm of scalars and vectors. - (vecN)(vecN)",
log2 = fn "returns the base-2 logarithm of scalars and vectors. - (vecN)(vecN)",
pow = fn "returns x to the y-th power of scalars and vectors. - (vecN)(vecN x, y)",
sqrt = fn "returns square root of scalars and vectors. - (vecN)(vecN)",
inversesqrt = fn "returns inverse square root of scalars and vectors. - (vecN)(vecN)",
abs = fn "returns absolute value of scalars and vectors. - (vecN)(vecN)",
sign = fn "returns sign (1 or -1) of scalar or each vector component. - (vecN)(vecN)",
floor = fn "returns largest integer not greater than a scalar or each vector component. - (vecN)(vecN)",
ceil = fn "returns smallest integer not less than a scalar or each vector component. - (vecN)(vecN)",
trunc = fn "returns largest integer not greater than a scalar or each vector component. - (vecN)(vecN)",
round = fn "returns the rounded value of scalars or vectors. - (vecN)(vecN a)",
roundEven = fn "returns the nearest even integer value of scalars or vectors. - (vecN)(vecN a)",
fract = fn "returns the fractional portion of a scalar or each vector component. - (vecN)(vecN)",
mod = fn "modulus - (vecN)(vecN x, y)",
modf = fn "separate integer and fractional parts. - (vecN)(vecN x, out vecN i)",
max = fn "returns the maximum of two scalars or each respective component of two vectors. - (vecN)(vecN a, b)",
min = fn "returns the minimum of two scalars or each respective component of two vectors. - (vecN)(vecN a, b)",
mix = fn "returns linear interpolation of two scalars or vectors based on a weight. - (vecN)(vecN a, b, weight)",
step = fn "implement a step function returning either zero or one (x >= edge). - (vecN)(vecN edge, x)",
isinf = fn "test whether or not a scalar or each vector component is infinite. - (boolN)(vecN)",
isnan = fn "test whether or not a scalar or each vector component is not-a-number. - (boolN)(vecN)",
clamp = fn "returns x clamped to the range [a,b]. - (vecN)(vecN x, a, b)",
smoothstep = fn "clip and smooth blend [a,b]. - (vecN)(vecN a, b, x)",
floatBitsToInt = fn "returns the 32-bit integer representation of an IEEE 754 floating-point scalar or vector - (uintN/intN)(floatN)",
intBitsToFloat = fn "returns the float value corresponding to a given bit represention.of a scalar int value or vector of int values. - (floatN)(intN)",
uintBitsToFloat = fn "returns the float value corresponding to a given bit represention.of a scalar int value or vector of int values. - (floatN)(uintN)",
doubleBitsToInt64 = fn "returns the 64-bit integer representation of an IEEE 754 double precision floating-point scalar or vector - (int64N)(doubleN)",
doubleBitsToUint64 = fn "returns the 64-bit integer representation of an IEEE 754 double precision floating-point scalar or vector - (uint64N)(doubleN)",
int64BitsToDouble = fn "returns the double value corresponding to a given bit represention.of a scalar int value or vector of int values. - (doubleN)(uint64N)",
uint64BitsToDouble = fn "returns the double value corresponding to a given bit represention.of a scalar int value or vector of int values. - (doubleN)(uint64N)",
fma = fn "return a*b + c, treated as single operation when using precise - (vecN a, vecN b, vecN c)",
frexp = fn "splits scalars and vectors into normalized fraction [0.5,1.0) and a power of 2. - (vecN)(vecN x, out vecN e)",
ldexp = fn "build floating point number from x and the corresponding integral exponen of 2 in exp. - (vecN)(vecN x, exp)",
packUnorm2x16 = fn "Converts each comp. of v into 16-bit ints, packs results into the returned 32-bit uint. - (uint)(vec2 v)",
packUnorm4x8 = fn "Converts each comp. of v into 8-bit ints, packs results into the returned 32-bit uint. - (uint)(vec4 v)",
packSnorm4x8 = fn "Converts each comp. of v into 8-bit ints, packs results into the returned 32-bit uint. - (uint)(vec4 v)",
packDouble2x32 = fn "Packs components of v into a 64-bit value and returns a double-prec value. - (double)(uvec2 v)",
packHalf2x16 = fn "Converts each comp. of v into 16-bit half float, packs results into the returned 32-bit uint. - (uint)(vec2 v)",
packInt2x32 = fn "Packs two 32 bit into one 64-bit value. - (int64_t)(ivec2)",
packUint2x32 = fn "Packs two 32 bit into one 64-bit value. - (uint64_t)(uvec2)",
packFloat2x16 = fn "returns an unsigned integer obtained by interpreting the components of a two-component 16-bit floating-point as integers and packing them into 32 bit. - (uint)(f16vec2 v)",
unpackUnorm2x16 = fn "Unpacks 32-bit p into two 16-bit uints and converts them to normalized float. - (vec2)(uint p)",
unpackUnorm4x8 = fn "Unpacks 32-bit p into four 8-bit uints and converts them to normalized float. - (vec4)(uint p)",
unpackSnorm4x8 = fn "Unpacks 32-bit p into four 8-bit uints and converts them to normalized float. - (vec4)(uint p)",
unpackDouble2x32 = fn "Returns a 2 component vector representation of v. - (uvec2)(double v)",
unpackHalf2x16 = fn "Interprets p as two 16-bit half floats and returns them as vector. - (vec2)(uint p)",
unpackInt2x32 = fn "Unpacks 64-bit into two 32-bit values. - (ivec2)(int64_t)",
unpackUint2x32 = fn "Unpacks 64-bit into two 32-bit values. - (uvec2)(uint64_t)",
unpackFloat2x16 = fn "returns a two-component vector with 16-bit floating-point components obtained by unpacking a 32-bit unsigned integer into a pair of 16-bit values. - (f16vec2)(uint)",
length = fn "return scalar Euclidean length of a vector. - (type)(vecN)",
distance = fn "return the Euclidean distance between two points. - (vecN)(vecN a, b)",
dot = fn "returns the scalar dot product of two vectors. - (type)(vecN a, b)",
cross = fn "returns the cross product of two three-component vectors. - (type3)(type3 a, b)",
normalize = fn "Returns the normalized version of a vector, meaning a vector in the same direction as the original vector but with a Euclidean length of one. - (vecN)(vecN)",
reflect = fn "returns the reflectiton vector given an incidence vector and a normal vector. - (vecN)(vecN incidence, normal)",
refract = fn "computes a refraction vector. - (vecN)(vecN incidence, normal, type eta)",
faceforward = fn "returns a normal as-is if a vertex's eye-space position vector points in the opposite direction of a geometric normal, otherwise return the negated version of the normal. - (vecN)(vecN Nperturbated, Incident, Ngeometric)",
determinant = fn "returns the scalar determinant of a square matrix. - (float)(matN)",
transpose = fn "returns transpose matrix of a matrix. - (matNxM)(matMxN)",
inverse = fn "returns inverse matrix of a matrix. - (matN)(mat)",
matrixCompMult = fn "component-wise multiply. - (mat)(mat a, b)",
outerProduct = fn "outer product. - (matNxM)(vecM c, vecN r)",
all = fn "returns true if a boolean scalar or all components of a boolean vector are true. - (bool)(boolN)",
any = fn "returns true if a boolean scalar or any component of a boolean vector is true. - (bool)(boolN)",
["not"] = fn "returns logical complement. - (boolN)(boolN)",
lessThan = fn "returns retusult of component-wise comparison. - (boolN)(vecN a,b)",
lessThanEqual = fn "returns retusult of component-wise comparison. - (boolN)(vecN a,b)",
greaterThan = fn "returns retusult of component-wise comparison. - (boolN)(vecN a,b)",
greaterThanEqual = fn "returns retusult of component-wise comparison. - (boolN)(vecN a,b)",
equal = fn "returns retusult of component-wise comparison. - (boolN)(vecN a,b)",
notEqual = fn "returns retusult of component-wise comparison. - (boolN)(vecN a,b)",
uaddCarry = fn "Adds 32-bit uintx and y, returning the sum modulo 2^32. - (uintN)(uintN x, y, out carry)",
usubBorrow = fn "Subtracts y from x, returning the difference if non-negative otherwise 2^32 plus the difference. - (uint)(uint x, y, out borrow)",
umulExtended = fn "Multiplies 32-bit integers x and y producing 64-bit result. (uintN)(uintN x, y, out msb, out lsb)",
imulExtended = fn "Multiplies 32-bit integers x and y producing 64-bit result. (intN)(intN x, y, out msb, out lsb)",
bitfieldExtract = fn "Extracts bits (offset, offset + bits -1) from value and returns them in lsb of result. - (intN)(intN value, int offset, int bits)",
bitfieldInsert = fn "Returns the insertion the bits lsb of insert into base. - (intN)(intN base insert, int offset, int bits)",
bitfieldReverse = fn "Returns the reversal of the bits. - (intN)(intN)",
bitCount = fn "returns the number of bits set to 1. - (intN)(intN)",
findLSB = fn "returns bit number of lsb. - (intN)(intN)",
findMSB = fn "returns bit number of msb. - (intN)(intN)",
discard = fn "conditionally (<0) kill a pixel before output. - ()(vecN)",
dFdx = fn "returns approximate partial derivative with respect to window-space X. - (vecN)(vecN)",
dFdxCoarse = fn "returns approximate partial derivative with respect to window-space X. - (vecN)(vecN)",
dFdxFine = fn "returns approximate partial derivative with respect to window-space X. - (vecN)(vecN)",
dFdy = fn "returns approximate partial derivative with respect to window-space Y. - (vecN)(vecN)",
dFdyCoarse = fn "returns approximate partial derivative with respect to window-space Y. - (vecN)(vecN)",
dFdyFine = fn "returns approximate partial derivative with respect to window-space Y. - (vecN)(vecN)",
fwidth = fn "returns abs sum of approximate window-space partial derivatives magnitudes. - (vecN)(vecN)",
fwidthFine = fn "returns abs sum of approximate window-space partial derivatives magnitudes. - (vecN)(vecN)",
fwidthCoarse = fn "returns abs sum of approximate window-space partial derivatives magnitudes. - (vecN)(vecN)",
interpolateAtCentroid = fn "Return value of interpolant sampled inside pixel and the primitive. - (floatN)(floatN)",
interpolateAtSample = fn "Return value of interpolant at the location fo sample. - (floatN)(floatN, int sample)",
interpolateAtOffset = fn "Return value of interpolant sampled at fixed offset offset from pixel center. - (floatN)(floatN, vec2 offset)",
noise1 = fn "returns noise value. - (float)(float)",
noise2 = fn "returns noise value. - (vec2)(float)",
noise3 = fn "returns noise value. - (vec3)(float)",
noise4 = fn "returns noise value. - (vec4)(float)",
EmitStreamVertex = fn "Emits values of the output variables of the current output primitive stream. - ()(int stream)",
EndStreamPrimitive = fn "Completes current output primitive stream and starts a new one. - ()(int stream)",
EmitVertex= fn "Emits values of the output variable of the current output primitive. - ()()",
EndPrimitive = fn "Completes current output primitive and starts a new one. - ()()",
barrier = fn "Synchronizes across shader invocations. - ()()",
memoryBarrier = fn "control ordering of memory transactions issued by shader thread. - ()()",
memoryBarrierAtomicCounter = fn "control ordering of memory transactions issued by shader thread. - ()()",
memoryBarrierShared = fn "control ordering of memory transactions issued by shader thread. - ()()",
memoryBarrierBuffer = fn "control ordering of memory transactions issued by shader thread. - ()()",
memoryBarrierImage = fn "control ordering of memory transactions issued by shader thread. - ()()",
groupMemoryBarrier = fn "control ordering of memory transactions issued by shader thread. - ()()",
imageAtomicAdd = fn "performs atomic operation on individual texels returns old value. - (uint)(imageN, intN coord, [int sample], uint data)",
imageAtomicMin = fn "performs atomic operation on individual texels returns old value. - (uint)(imageN, intN coord, [int sample], uint data)",
imageAtomicMax = fn "performs atomic operation on individual texels returns old value. - (uint)(imageN, intN coord, [int sample], uint data)",
imageAtomicIncWrap = fn "performs atomic operation on individual texels returns old value. - (uint)(imageN, intN coord, [int sample], uint data)",
imageAtomicDecWrap = fn "performs atomic operation on individual texels returns old value. - (uint)(imageN, intN coord, [int sample], uint data)",
imageAtomicAnd = fn "performs atomic operation on individual texels returns old value. - (uint)(imageN, intN coord, [int sample], uint data)",
imageAtomicOr = fn "performs atomic operation on individual texels returns old value. - (uint)(imageN, intN coord, [int sample], uint data)",
imageAtomicXor = fn "performs atomic operation on individual texels returns old value. - (uint)(imageN, intN coord, [int sample], uint data)",
imageAtomicExchange = fn "performs atomic operation on individual texels returns old value. - (uint)(imageN, intN coord, [int sample], uint data)",
imageAtomicCompSwap = fn "performs atomic operation on individual texels returns old value. - (uint)(imageN, intN coord, [int sample], uint data)",
imageStore = fn "stores the texel at the coordinate. - ()(imageN, intN coord, [int sample], vecN data)",
imageLoad = fn "loads the texel at the coordinate. - (vecN)(imageN, intN coord, [int sample])",
imageSize = fn "returns the size of the image. - (ivecN)(imageN)",
imageSamples = fn "returns the samples of the multi-sampled image. - (int)(image2DMSN)",
atomicCounterIncrement = fn "increments counter and returns old value. - (uint)(atomic_uint)",
atomicCounterDecrement = fn "decrements counter and returns old value. - (uint)(atomic_uint)",
atomicCounter = fn "returns current counter value. - (uint)(atomic_uint)",
atomicMin = fn "performs atomic operation on memory location (ssbo/shared) returns old value. - (uint)(inout uint mem, uint data)",
atomicMax = fn "performs atomic operation on memory location (ssbo/shared) returns old value. - (uint)(inout uint mem, uint data)",
atomicAdd = fn "performs atomic operation on memory location (ssbo/shared) returns old value. - (uint)(inout uint mem, uint data)",
atomicAnd = fn "performs atomic operation on memory location (ssbo/shared) returns old value. - (uint)(inout uint mem, uint data)",
atomicOr = fn "performs atomic operation on memory location (ssbo/shared) returns old value. - (uint)(inout uint mem, uint data)",
atomicXor = fn "performs atomic operation on memory location (ssbo/shared) returns old value. - (uint)(inout uint mem, uint data)",
atomicExchange = fn "performs atomic operation on memory location (ssbo/shared) returns old value. - (uint)(inout uint mem, uint data)",
atomicCompSwap = fn "performs atomic operation on memory location (ssbo/shared) returns old value. - (uint)(inout uint mem, uint data)",
textureSize = fn "returns the size of the texture (no lod required: Rect, MS and Buffer). - (intN)(samplerN, [int lod])",
textureSamples = fn "returns the samples of the multi-sampled texture. - (int)(texture2DMSN)",
textureQueryLod = fn "returns the lod values for a given coordinate. - (vec2)(samplerN, vecN coord)",
texture = fn "performs a texture lookup. Shadow samplers require base N+1 coordinate. Lod bias is optional (illegal for MS, Buffer, Rect). - (vec4)(samplerN, vecN coord, [float bias])",
textureProj = fn "performas a projective texture lookup (only Nd samplers + Rect). Shadows require N+1 base coordinate, no Lod bias allowed for Rect. - (vec4)(samplerN, vecN+1 coord, [float bias])",
textureLod = fn "performs a lookup with explicit LOD. Shadows require N+1 base coordinate. Illegal function for Rect, MS, Buffer. - (vec4)(samplerN, vecN coord, float lod)",
textureOffset = fn "offset added before texture lookup. Illegal for MS, Buffer, Cube. - (vec4)(samplerN, vecN coord, intN offset, [float bias])",
textureProjOffset = fn "projective texture lookup with offset. Illegal for MS, Buffer, Cube, Array. - (vec4)(samplerN, vecN+1 coord, intN offset, [float bias])",
textureLodOffset = fn "offset added with explicit LOD. - (vec4)(samplerN, vecN coord, intN offset, int lod)",
textureProjLodOffset = fn "projective lookup with offset and explicit LOD. - (vec4)(samplerN, vecN+1 coord, intN offset, int lod)",
textureGrad = fn "lookup with explicit gradients. Illegal for MS, Buffer. - (vec4)(samplerN, vecN coord, gradX, gradY)",
textureGradOffset = fn "lookup with explicit gradients and offset. Illegal for MS, Buffer, Cube. - (vec4)(samplerN, vecN coord, gradX, gradY, intN offset)",
textureProjGradOffset = fn "projective lookup with expöicit gradients and offset. Illegal for MS, Buffer, Cube. - (vec4)(samplerN, vecN+1 coord, vecN gradX, gradY, intN offset)",
textureGather = fn "gather lookup (pixel quad of 4 single channel samples at once). Component 0: x, 1: y ... is ignored for shadow samplers instead reference value must be passed. Only 2D/Cube. Illegal for MS. - (vec4)(samplerN, vecN coord, [int comp] / float shadowRefZ)",
textureGatherOffset = fn "gather lookup (pixel quad of 4 single channel samples at once) with offset. Component 0: x, 1: y ... is ignored for shadow samplers instead reference value must be passed. Only 2D/Cube. Illegal for MS. - (vec4)(samplerN, vecN coord, [float shadowRefZ], intN offset / intN offset[4] , [int comp])",
texelFetch = fn "integer coordinate lookup for a single texel. No lod parameter for Buffer, MS, Rect. Illegal for Cube - (vec4)(samplerN, intN coord, [int lod/sample])",
texelFetchOffset = fn "integer coordinate lookup for a single texel with offset. No lod parameter for Buffer, MS, Rect. Illegal for Cube, Buffer, MS. - (vec4)(samplerN, intN coord, [int lod/sample], intN offset)",
anyInvocationARB = fn "returns true if and only if <value> is true for at least one active invocation in the group. - (bool)(bool value)",
allInvocationsARB = fn "returns true if and only if <value> is true for all active invocations in the group - (bool)(bool value)",
allInvocationsEqualARB = fn "returns true if <value> is the same for all active invocation in the group. - (bool)(bool value)",
}
local keyw =
[[ int uint half float bool double atomic_uint binding offset
vec2 vec3 vec4 dvec2 dvec3 dvec4
ivec2 ivec3 ivec4 uvec2 uvec3 uvec4 bvec2 bvec3 bvec4
mat2 mat3 mat4 mat2x2 mat3x3 mat4x4 mat2x3 mat3x2 mat4x2 mat2x4 mat4x3 mat3x4
dmat2 dmat3 dmat4 dmat2x2 dmat3x3 dmat4x4 dmat2x3 dmat3x2 dmat4x2 dmat2x4 dmat4x3 dmat3x4
float16_t f16vec2 f16vec3 f16vec4
float32_t f32vec2 f32vec3 f32vec4
float64_t f64vec2 f64vec3 f64vec4
int8_t i8vec2 i8vec3 i8vec4
int8_t i8vec2 i8vec3 i8vec4
int16_t i16vec2 i16vec3 i16vec4
int32_t i32vec2 i32vec3 i32vec4
int64_t i64vec2 i64vec3 i64vec4
uint8_t u8vec2 u8vec3 u8vec4
uint16_t u16vec2 u16vec3 u16vec4
uint32_t u32vec2 u32vec3 u32vec4
uint64_t u64vec2 u64vec3 u64vec4
struct typedef void
usampler1D usampler2D usampler3D usampler2DRect usamplerCube isampler1DArray usampler2DARRAY usamplerCubeArray usampler2DMS usampler2DMSArray
isampler1D isampler2D isampler3D isampler2DRect isamplerCube isampler1DArray isampler2DARRAY isamplerCubeArray isampler2DMS isampler2DMSArray
sampler1D sampler2D sampler3D sampler2DRect samplerCube sampler1DArray sampler2DArray samplerCubeArray sampler2DMS sampler2DMSArray
sampler1DShadow sampler2DShadow sampler2DRectShadow sampler1DArrayShadow sampler2DArrayShadow samplerCubeArrayShadow
usamplerBuffer isamplerBuffer samplerBuffer samplerRenderbuffer isamplerRenderbuffer usamplerRenderbuffer
in out inout uniform const centroid sample attribute varying patch index true false
return switch case for do while if else break continue main inline
layout location vertices line_strip triangle_strip max_vertices stream
triangles quads equal_spacing isolines fractional_even_spacing lines points
fractional_odd_spacing cw ccw point_mode lines_adjacency triangles_adjacency
invocations offset align xfb_offset xfb_buffer
origin_upper_left pixel_center_integer depth_greater depth_greater depth_greater depth_unchanged
smooth flat noperspective highp mediump lowp shared packed std140 std430 row_major column_major buffer
gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor gl_Color gl_SecondaryColor
subroutine gl_Position gl_FragCoord
gl_VertexID gl_InstanceID gl_Normal gl_Vertex gl_MultiTexCoord0 gl_MultiTexCoord1
gl_MultiTexCoord2 gl_MultiTexCoord3 gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6
gl_MultiTexCoord7 gl_FogCoord gl_PointSize gl_ClipDistance
gl_TexCoord gl_FogFragCoord gl_ClipVertex gl_in
gl_PatchVerticesIn
gl_PrimitiveID gl_InvocationID gl_TessLevelOuter gl_TessLevelInner gl_TessCoord
gl_InvocationID gl_PrimitiveIDIn gl_Layer gl_ViewportIndex gl_FrontFacing
gl_PointCoord gl_SampleID gl_SamplePosition gl_FragColor
gl_FragData gl_FragDepth gl_SampleMask
gl_NumWorkGroups gl_WorkGroupSize gl_WorkGroupID gl_LocalInvocationID gl_GlobalInvocationID gl_LocalInvocationIndex
local_size_x local_size_y local_size_z
gl_BaseVertexARB gl_BaseInstanceARB gl_DrawIDARB
bindless_sampler bound_sampler bindless_image bound_image early_fragment_tests
gl_HelperInvocation gl_CullDistance gl_MaxSamples
coherent volatile restrict readonly writeonly
image1D image2D image3D image2DRect imageCube imageBuffer image1DArray image2DArray imageCubeArray image2DMS image2DMSArray
uimage1D uimage2D uimage3D uimage2DRect uimageCube uimageBuffer uimage1DArray uimage2DArray uimageCubeArray uimage2DMS uimage2DMSArray
iimage1D iimage2D iimage3D iimage2DRect iimageCube iimageBuffer iimage1DArray iimage2DArray iimageCubeArray iimage2DMS iimage2DMSArray
size1x8 size1x16 size1x32 size2x32 size4x32 rgba32f rgba16f rg32f rg16f r32f r16f rgba8 rgba16 r11f_g11f_b10f rgb10_a2ui
rgb10_a2i rg16 rg8 r16 r8 rgba32i rgba16i rgba8i rg32i rg16i rg8i r32i r16i r8i rgba32ui rgba16ui rgba8ui rg32ui rg16ui rg8ui
r32ui r16ui r8ui rgba16_snorm rgba8_snorm rg16_snorm rg8_snorm r16_snorm r8_snorm
]]
-- keywords - shouldn't be left out
for w in keyw:gmatch("([a-zA-Z_0-9]+)") do
api[w] = {type="keyword"}
end
return api

View File

@ -1,52 +0,0 @@
-- authors: Luxinia Dev (Eike Decker & Christoph Kubisch)
---------------------------------------------------------
-- function helpers
local function fn (description)
local description2,returns,args = description:match("(.+)%-%s*(%b())%s*(%b())")
if not description2 then
return {type="function",description=description,
returns="(?)"}
end
returns = returns:gsub("^%s+",""):gsub("%s+$","")
local ret = returns:sub(2,-2)
local vt = ret:match("^%[?string") and "string"
vt = vt or ret:match("^%[?table") and "table"
vt = vt or ret:match("^%[?file") and "io"
return {type="function",description=description2,
returns=returns, args = args, valuetype = vt}
end
local function val (description)
return {type="value",description = description}
end
---------------------------
local api = {
ffi = {
description = "FFI",
type = "lib",
childs = {
cdef = fn "Adds multiple C declarations for types or external symbols - ()(string)",
load = fn "This loads the dynamic library given by name and returns a new C library namespace which binds to its symbols. On POSIX systems, if global is true, the library symbols are loaded into the global namespace, too. - (userdata)(string,[global])",
new = fn "The following API functions create cdata objects (type() returns 'cdata'). All created cdata objects are garbage collected. - (cdata)(string/ctype,nelement,init...)",
typeof = fn "Creates a ctype object for the given ct. - (ctype)(ct)",
cast = fn "Creates a scalar cdata object for the given ct. The cdata object is initialized with init according to C casting rules. - (cdata)(ctype,cdata init)",
metatype = fn "Creates a ctype object for the given ct and associates it with a metatable. Only struct/union types, complex numbers and vectors are allowed. Other types may be wrapped in a struct, if needed. - (cdata)(ct,table meta)",
gc = fn "Associates a finalizer with a pointer or aggregate cdata object. The cdata object is returned unchanged. - (cdata)(ct,function finalizer)",
sizeof = fn "Returns the size of ct in bytes. Returns nil if the size is not known. - (number)(ct,[nelem])",
alignof = fn "Returns the minimum required alignment for ct in bytes. - (number)(ct)",
offsetof = fn "Returns the offset (in bytes) of field relative to the start of ct, which must be a struct. Additionally returns the position and the field size (in bits) for bit fields. - (number)(ct, field)",
istype = fn "Returns true if obj has the C type given by ct. Returns false otherwise. - (boolean)(ct,obj)",
string = fn "Creates an interned Lua string from the data pointed to by ptr. If the optional argument len is missing, ptr is converted to a 'char *' and the data is assumed to be zero-terminated. The length of the string is computed with strlen(). - (string)(ptr, [number len])",
copy = fn "Copies the data pointed to by src to dst. dst is converted to a 'void *' and src is converted to a 'const void *'. - ()(dst,[src,len] / [string])",
fill = fn "Fills the data pointed to by dst with len constant bytes, given by c. If c is omitted, the data is zero-filled. - ()(dst, len, [c])",
abi = fn "Returns true if param (a Lua string) applies for the target ABI (Application Binary Interface). Returns false otherwise. 32bit 64bit lq be fpu softfp hardfp eabi win. - (boolean)(string)",
os = val "string value of OS",
}
},
}
return api

View File

@ -1,24 +0,0 @@
local function populateAPI(t)
local api = {}
for k,v in pairs(t) do
api[k] = {
type = (type(v) == "function" and "function" or "value"),
description = "",
returns = "",
}
end
return api
end
return {
wx = {
type = "lib",
description = "wx lib",
childs = populateAPI(wx),
},
wxstc = {
type = "lib",
description = "wxSTC lib",
childs = populateAPI(wxstc),
},
}

View File

@ -1,278 +0,0 @@
local funcstring =
[[
get_work_dim() Returns the number of dimensions in use
get_global_size(uint dimindx) Returns the number of global work-items specified for dimension identified by dimindx
get_global_id(uint dimindx) Returns the unique global work-item ID value for dimension identified by dimindx
get_local_size(uint dimindx) Returns the number of local work-items specified in dimension identified by dimindx
get_local_id(uint dimindx) Returns the unique local work-item ID i.e. a work-item within a specific work-group for dimension identified by dimindx.
get_num_groups(uint dimindx) Returns the number of work-groups that will execute a kernel for dimension identified by dimindx
get_group_id(uint dimindx) Returns the work-group ID
acos(gentype) Arc cosine function
acosh(gentype) Inverse hyperbolic cosine
acospi(gentype) Compute acos (x) / PI
asin(gentype) Arc sine function
asinh(gentype) Inverse hyperbolic sine
asinpi(gentype x) Compute asin (x) / PI
atan(gentype y_over_x) Arc tangent function
atan2(gentype y, gentype x) Arc tangent of y / x
atanh(gentype) Hyperbolic arc tangent.
atanpi(gentype x) Compute atan (x) / PI
atan2pi(gentype y, gentype x) Compute atan2 (y, x) / PI
cbrt(gentype) Compute cube-root
ceil(gentype) Round to integral value using the round to +ve infinity rounding mode
copysign(gentype x, gentype y) Returns x with its sign changed to match the sign of y
cos(gentype) Compute cosine
cosh(gentype) Compute hyperbolic consine
cospi(gentype x) Compute cos (PI*x)
erfc(gentype) Complementary error function
erf(gentype) Error function encountered in integrating the normal distribution
exp(gentype x) Compute the base- e exponential of x
exp2(gentype) Exponential base 2 function
exp10(gentype) Exponential base 10 function
expm1(gentype x) Compute e^x - 1.0
fabs(gentype) Compute absolute value of a floating-point number
fdim(gentype x, gentype y) x - y if x > y, +0 if x is less than or equal to y
floor(gentype) Round to integral value using the round to ve infinity rounding mode
fma(gentype a, gentype b, gentype c) Returns the correctly rounded floating-point representation of the sum of c with the infinitely precise product of a and b
fmax(gentype x, gentype y) Returns y if x < y, otherwise it returns x
fmin(gentype x, gentype y) Returns y if y < x, otherwise it returns x
fmod(gentype x, gentype y) Modulus. Returns x y * trunc (x/y)
fract(gentype x, gentype *iptr) Returns fmin( x floor (x), 0x1.fffffep-1f ).
frexp(gentype x, intn *exp) Extract mantissa and exponent from x
hypot(gentype x, gentype y) Compute the value of the square root of x2+y2
ilogb(gentype x) Return the exponent as an integer value
ldexp(gentype x, intn n) Multiply x by 2 to the power n
lgamma(gentype x) Returns the natural logarithm of the absolute value of the gamma function
lgamma_r(gentype x, intn *signp) Returns the natural logarithm of the absolute value of the gamma function
log(gentype) Compute natural logarithm
log2(gentype) Compute a base 2 logarithm
log10(gentype) Compute a base 10 logarithm
log1p(gentype x) Compute loge(1.0 + x)
logb(gentype x) Compute the exponent of x, which is the integral part of logr|x|
mad(gentype a, gentype b, gentype c) Approximates a * b + c.
modf(gentype x, gentype *iptr) Decompose a floating-point number
nan(uintn nancode) Returns a quiet NaN
nextafter(gentype x, gentype y) Computes the next representable single-precision floating-point value following x in the direction of y.
pow(gentype x, gentype y) Compute x to the power y
pown(gentype x, intn y) Compute x to the power y, where y is an integer
powr(gentype x, gentype y) Compute x to the power y, where x is >= 0
remainder(gentype x, gentype y) r = x - n*y, where n is the integer nearest the exact value of x/y
remquo(gentype x, gentype y, intn *quo) r = x - n*y, where n is the integer nearest the exact value of x/y
rint(gentype) Round to integral value (using round to nearest even rounding mode)
rootn(gentype x, intn y) Compute x to the power 1/y
round(gentype x) Return the integral value nearest to x rounding halfway cases away from zero
rsqrt(gentype) Compute inverse square root
sin(gentype) Compute sine
sincos(gentype x, gentype *cosval) Compute sine and cosine of x
sinh(gentype) Compute hyperbolic sine.
sinpi(gentype x) Compute sin (PI*x)
sqrt(gentype) Compute square root
tan(gentype) Compute tangent
tanh(gentype) Compute hyperbolic tangent
tanpi(gentype x) Compute tan (PI*x)
tgamma(gentype) Compute the gamma function
trunc(gentype) Round to integral value using the round to zero
abs(gentype x) Returns |x|
abs_diff(gentype x, gentype y) Returns |x y| without modulo overflow
add_sat(gentype x, gentype y) Returns x + y and saturates the result
hadd(gentype x, gentype y) Returns (x + y) >> 1
rhadd(gentype x, gentype y) Returns (x + y + 1) >> 1
clz(gentype x) Returns the number of leading 0-bits in x, starting at the most significant bit position.
mad_hi(gentype a, gentype b, gentype c) Returns mul_hi(a, b) + c
mad_sat(gentype a, gentype b, gentype c) Returns a * b + c and saturates the result
max(gentype x, gentype y) Returns y if x < y, otherwise it returns x
min(gentype x, gentype y) Returns y if y < x, otherwise it returns x
mul_hi(gentype x, gentype y) Computes x * y and returns the high half of the product of x and y
rotate(gentype v, gentype i)
sub_sat(gentype x, gentype y) Returns x - y and saturates the result
upsample(charn hi, ucharn lo) result[i] = ((short)hi[i] << 8) | lo[i]
mad24(gentype x, gentype y, gentype z)
mul24(gentype x, gentype y)
clamp(gentype x, gentype minval, gentype maxval) Returns fmin(fmax(x, minval), maxval)
degrees(gentype radians) Converts radians to degrees
max(gentype x, gentype y)
min(gentype x, gentype y)
mix(gentype x, gentype y, gentype a) Returns the linear blend of x&y: x + (y x) * a
radians(gentype degrees) Converts degrees to radians
step(gentype edge, gentype x) Returns 0.0 if x < edge, otherwise it returns 1.0
smoothstep(genType edge0, genType edge1, genType x)
sign(gentype x)
cross(float4 p0, float4 p1) Returns the cross product of p0.xyz and p1.xyz.
dot(gentype p0, gentype p1) Compute dot product
distance(gentype p0, gentype p1) Returns the distance between p0 and p1
length(gentype p) Return the length of vecto
normalize(gentype p) Returns a vector in the same direction as p but with length of 1.
fast_distance(gentype p0, gentype p1) Returns fast_length(p0 p1).
fast_length(gentype p) Returns the length of vector
fast_normalize(gentype p) Returns a vector in the same direction as p but with length of 1.
read_imagef(image2d_t image, sampler_t sampler, int2 coord)
read_imagei(image2d_t image, sampler_t sampler, int2 coord)
read_imageui(image2d_t image, sampler_t sampler, int2 coord)
write_imagef(image2d_t image, int2 coord, float4 color)
write_imagei(image2d_t image, int2 coord, int4 color)
write_imageui(image2d_t image, int2 coord, unsigned int4 color)
get_image_width(image2d_t image)
get_image_width(image3d_t image)
get_image_height(image2d_t image)
get_image_height(image3d_t image)
get_image_channel_data_type(image2d_t image)
get_image_channel_data_type(image3d_t image)
get_image_channel_order(image2d_t image)
get_image_channel_order(image3d_t image)
get_image_dim(image2d_t image)
get_image_dim(image3d_t image)
barrier(cl_mem_fence_flags flags) All work-items in a work-group executing the kernel must execute this function before any are allowed to continue execution beyond the barrier.
mem_fence(cl_mem_fence_flags flags) Orders loads and stores of a work-item executing a kernel.
read_mem_fence(cl_mem_fence_flags flags) Read memory barrier that orders only loads.
write_mem_fence(cl_mem_fence_flags flags) Write memory barrier that orders only stores.
async_work_group_copy(gentype *dst, const gentype *src, size_t num_elements, event_t event) Perform an async copy of num_elements gentype elements from src to dst.
wait_group_events(int num_events, event_t *event_list) Wait for events that identify the async_work_group_copy operations to complete.
prefetch(const __global gentype *p, size_t num_elements) Prefetch num_elements * sizeof(gentype) bytes into the global cache.
vload2(size_t offset, const type *p) Read vector data from memory
vload4(size_t offset, const type *p) Read vector data from memory
vload8(size_t offset, const type *p) Read vector data from memory
vload16(size_t offset, const type *p) Read vector data from memory
vstore2(type2 data, size_t offset, type *p) Write vector data to memory
vstore4(type4 data, size_t offset, type *p) Write vector data to memory
vstore8(type8 data, size_t offset, type *p) Write vector data to memory
vstore16(type16 data, size_t offset, type *p) Write vector data to memory
]]
local function fn (description)
local description2,returns,args = description:match("(.+)%-%s*(%b())%s*(%b())")
if not description2 then
return {type="function",description=description,
returns="(?)"}
end
return {type="function",description=description2,
returns=returns:gsub("^%s+",""):gsub("%s+$",""), args = args}
end
local function val (description)
return {type="value",description = description}
end
-- docs
local api = {
}
local convtypes = [[bool char uchar short ushort int uint long ulong float double]]
local convout = {}
for i in convtypes:gmatch("([%w_]+)") do
local suffix = {"","_rte","_rtz","_rtp","_rtn"}
for k,t in ipairs(suffix) do
table.insert(convout,"convert_"..i..t)
table.insert(convout,"convert_"..i.."_sat"..t)
local vectors = {2,4,8,16}
for n,v in ipairs(vectors) do
table.insert(convout,"convert_"..i..v..t)
table.insert(convout,"convert_"..i..v.."_sat"..t)
end
end
end
convout = table.concat(convout, " ")
local astypes = [[int uint uchar ushort float double size_t ptrdiff_t intptr_t uintptr_t
long ulong char short unsigned
float2 float4 float8 float16
double2 double4 double8 double16
char2 char4 char8 char16
uchar2 uchar4 uchar8 uchar16
short2 short4 short8 short16
ushort2 ushort4 ushort8 ushort16
int2 int4 int8 int16
uint2 uint4 uint8 uint16
long2 long4 long8 long16
ulong2 ulong4 ulong8 ulong16]]
local astypeout = {}
for i in astypes:gmatch("([%w_]+)") do
table.insert(astypeout, "as_"..i)
end
astypeout = table.concat(astypeout, " ")
local keyw = astypeout.." "..convout.." "..[[
int uint uchar ushort half float bool double size_t ptrdiff_t intptr_t uintptr_t void
long ulong char short unsigned
half2 half4 half8 half16
float2 float4 float8 float16
double2 double4 double8 double16
char2 char4 char8 char16
uchar2 uchar4 uchar8 uchar16
short2 short4 short8 short16
ushort2 ushort4 ushort8 ushort16
int2 int4 int8 int16
uint2 uint4 uint8 uint16
long2 long4 long8 long16
ulong2 ulong4 ulong8 ulong16
image2d_t image3d_t sampler_t event_t cl_image_format
struct typedef void const
return switch case for do while if else break continue volatile
CLK_A CLK_R CLK_RG CLK_RGB CLK_RGBA CLK_ARGB CLK_BGRA CLK_INTENSITY CLK_LUMINANCE
MAXFLOAT HUGE_VALF INFINITY NAN
CLK_LOCAL_MEM_FENCE CLK_GLOBAL_MEM_FENCE
CLK_SNORM_INT8
CLK_SNORM_INT16
CLK_UNORM_INT8
CLK_UNORM_INT16
CLK_UNORM_SHORT_565
CLK_UNORM_SHORT_555
CLK_UNORM_SHORT_101010
CLK_SIGNED_INT8
CLK_SIGNED_INT16
CLK_SIGNED_INT32
CLK_UNSIGNED_INT8
CLK_UNSIGNED_INT16
CLK_UNSIGNED_INT32
CLK_HALF_FLOAT
CLK_FLOAT
__FILE__ __LINE__ __OPENCL_VERSION__ __ENDIAN_LITTLE__
__ROUNDING_MODE__ __IMAGE_SUPPORT__ __FAST_RELAXED_MATH__
__kernel kernel __attribute__ __read_only __write_only read_only write_only
__constant constant __local local __global global __private private
vec_type_hint work_group_size_hint reqd_work_group_size
aligned packed endian host device
async_work_group_copy wait_group_events prefetch
clamp min max degrees radians sign smoothstep step mix
mem_fence read_mem_fence write_mem_fence
cross prod distance dot length normalize fast_distance fast_length fast_normalize
read_image write_image get_image_width get_image_height get_image_depth
get_image_channel_data_type get_image_channel_order
get_image_dim
abs abs_diff add_sat clz hadd mad24 mad_hi mad_sat
mul24 mul_hi rhadd rotate sub_sat upsample
read_imagei write_imagei read_imageui write_imageui
read_imagef write_imagef
isequal isnotequal isgreater isgreaterequal isless islessequal islessgreater
isfinite isinf isnan isnormal isordered isunordered signbit any all bitselect select
acos acosh acospi asin asinh asinpi atan atan2 atanh atanpi atan2pi
cbrt ceil copysign cos half_cos native_cos cosh cospi half_divide native_divide
erf erfc exp half_exp native_exp exp2 half_exp2 native_exp2 exp10 half_exp10 native_exp10
expm1 fabs fdim floor fma fmax fmin fmod fract frexp hypot ilogb
ldexp lgamma lgamma_r log half_log native_log log2 half_log2 native_log2
log10 half_log10 native_log10 log1p logb mad modf nan nextafter
pow pown powr half_powr native_powr half_recip native_recip
remainder remquo rint round rootn rsqrt half_rsqrt native_rsqrt
sin half_sin native_sin sincos sinh sinpi sqrt half_sqrt native_sqrt
tan half_tan native_tan tanh tanpi tgamma trunc
barrier
vload2 vload4 vload8 vload16
vload_half vload_half2 vload_half4 vload_half8 vload_half16 vloada_half4 vloada_half8 vloada_half16
vstore2 vstore4 vstore8 vstore16
vstore_half vstore_half2 vstore_half4 vstore_half8 vstore_half16 vstorea_half4 vstorea_half8 vstorea_half16
get_global_id get_global_size get_group_id get_local_id get_local_size get_num_groups get_work_dim
]]
-- keywords - shouldn't be left out
for w in keyw:gmatch("([a-zA-Z_0-9]+)") do
api[w] = {type="keyword"}
end
return api

View File

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>lua</string>
<key>CFBundleDisplayName</key>
<string>Lua</string>
<key>CFBundleGetInfoString</key>
<string></string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>org.Lua.LuaInterpreter</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string></string>
<key>CFBundleName</key>
<string>Lua</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string></string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string></string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSRequiresCarbon</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string></string>
</dict>
</plist>

View File

@ -1,19 +0,0 @@
-- If you have used Estrela for graphics shader authoring or luxinia,
-- create/modify the `user.lua` file in the current folder
-- and add `include "estrela"` (1.21+) to load all tools and specifications by default again.
-- load all tools, specs, and interpreters
local all = function() return true end
load.tools(all)
load.specs(all)
load.interpreters(all)
-- this flag means that toggling between projects, will not affect the
-- list of opened files (old estrela default).
projectautoopen = false
-- default search paths for luxinia
local luxpath = os.getenv("LUXINIA")
path.luxinia = luxpath and luxpath.."/" or "../luxinia/engine/"
local luxpath2 = os.getenv("LUXINIA2")
path.luxinia2 = luxpath2 and luxpath2.."/" or "../luxinia2/runtime/bin_Windows_x86/"

View File

@ -1,345 +0,0 @@
return {
[0] = function(c) return c == 1 and 1 or 2 end, -- plural
["%s event failed: %s"] = "Ereignis fehlgeschlagen : %s", -- src\editor\package.lua
["%s%% formatted..."] = nil, -- src\editor\print.lua
["%s%% loaded..."] = "%s%% geladen...", -- src\editor\commands.lua
["&About"] = "&Über", -- src\editor\menu_help.lua
["&Add Watch"] = "&Beobachtungspunkt hinzufügen", -- src\editor\debugger.lua
["&Break"] = "&Unterbrechung", -- src\editor\menu_project.lua
["&Close Page"] = "S&eite schließen", -- src\editor\gui.lua, src\editor\menu_file.lua
["&Community"] = "&Community", -- src\editor\menu_help.lua
["&Compile"] = "&Compiler", -- src\editor\menu_project.lua
["&Copy Value"] = "Wert kopieren", -- src\editor\debugger.lua
["&Copy"] = "&Kopieren", -- src\editor\gui.lua, src\editor\editor.lua, src\editor\menu_edit.lua
["&Default Layout"] = "Standard-&Layout", -- src\editor\menu_view.lua
["&Delete Watch"] = "&Beobachtungspunkt entfernen", -- src\editor\debugger.lua
["&Delete"] = "&Entfernen", -- src\editor\filetree.lua
["&Documentation"] = "&Dokumentation", -- src\editor\menu_help.lua
["&Edit Project Directory"] = "Projektverzeichnis ändern", -- src\editor\filetree.lua
["&Edit Value"] = "Wert editieren", -- src\editor\debugger.lua
["&Edit Watch"] = "&Beobachtungspunkt bearbeiten", -- src\editor\debugger.lua
["&Edit"] = "&Bearbeiten", -- src\editor\menu_edit.lua
["&File"] = "&Datei", -- src\editor\menu_file.lua
["&Find"] = "&Finden", -- src\editor\menu_search.lua
["&Fold/Unfold All"] = "A&lles ein-/ausklappen", -- src\editor\menu_edit.lua
["&Frequently Asked Questions"] = "&FAQ", -- src\editor\menu_help.lua
["&Getting Started Guide"] = "&Anfängerleitfaden", -- src\editor\menu_help.lua
["&Help"] = "&Hilfe", -- src\editor\menu_help.lua
["&New Directory"] = "&Neuer Ordner", -- src\editor\filetree.lua
["&New"] = "&Neu", -- src\editor\menu_file.lua
["&Open..."] = "&Öffnen...", -- src\editor\menu_file.lua
["&Output/Console Window"] = "&Ausgabefenster/Konsole", -- src\editor\menu_view.lua
["&Paste"] = "&Einfügen", -- src\editor\gui.lua, src\editor\editor.lua, src\editor\menu_edit.lua
["&Print..."] = nil, -- src\editor\print.lua
["&Project Page"] = "&Projektseite", -- src\editor\menu_help.lua
["&Project"] = "&Projekt", -- src\editor\menu_project.lua
["&Redo"] = "&Wiederholen", -- src\editor\gui.lua, src\editor\editor.lua, src\editor\menu_edit.lua
["&Rename"] = "&Umbenennen", -- src\editor\filetree.lua
["&Replace"] = "&Ersetzen", -- src\editor\menu_search.lua
["&Run"] = "&Starten", -- src\editor\menu_project.lua
["&Save"] = "&Speichern", -- src\editor\gui.lua, src\editor\menu_file.lua
["&Search"] = "&Suchen", -- src\editor\menu_search.lua
["&Select Command"] = nil, -- src\editor\gui.lua
["&Sort"] = "&Sortieren", -- src\editor\menu_edit.lua
["&Stack Window"] = "&Stapel/Stack", -- src\editor\menu_view.lua
["&Start Debugger Server"] = "De&bugserver starten", -- src\editor\menu_project.lua
["&Status Bar"] = "S&tatuszeile", -- src\editor\menu_view.lua
["&Tool Bar"] = "&Werkzeugleiste", -- src\editor\menu_view.lua
["&Tutorials"] = "&Tutorien", -- src\editor\menu_help.lua
["&Undo"] = "&Rückgängig", -- src\editor\gui.lua, src\editor\editor.lua, src\editor\menu_edit.lua
["&View"] = "&Ansicht", -- src\editor\menu_view.lua
["&Watch Window"] = "&Beobachtungspunkte", -- src\editor\menu_view.lua
["About %s"] = "Über %s", -- src\editor\menu_help.lua
["Add To Scratchpad"] = "Zu Entwurf hinzufügen", -- src\editor\editor.lua
["Add Watch Expression"] = "Beobachtungspunkt hinzufügen", -- src\editor\editor.lua
["All files"] = "Alle Dateien", -- src\editor\commands.lua
["Allow external process to start debugging"] = "Externem Prozeß erlauben, den Debugger zu starten", -- src\editor\menu_project.lua
["Analyze the source code"] = "Quellcode analysieren", -- src\editor\inspect.lua
["Analyze"] = "&Analyseroutine", -- src\editor\inspect.lua
["Auto Complete Identifiers"] = "Auto-Vervollständigen von Bezeichnern", -- src\editor\menu_edit.lua
["Auto complete while typing"] = "Auto-Vervollständigen beim Tippen", -- src\editor\menu_edit.lua
["Binary file is shown as read-only as it is only partially loaded."] = "Binärdatei ist als schreibgeschützt angezeigt, da sie nur teilweise geladen wurde.", -- src\editor\commands.lua
["Bookmark"] = "Lese&zeichen", -- src\editor\menu_edit.lua
["Break execution at the next executed line of code"] = "Programmausführung bei der nächsten ausgeführten Zeile stoppen", -- src\editor\toolbar.lua, src\editor\menu_project.lua
["Breakpoint"] = nil, -- src\editor\menu_project.lua
["C&lear Console Window"] = nil, -- src\editor\gui.lua
["C&lear Output Window"] = "Ausgabefenster l&öschen", -- src\editor\gui.lua, src\editor\menu_project.lua
["C&omment/Uncomment"] = "(Aus-)/K&ommentieren", -- src\editor\menu_edit.lua
["Can't evaluate the expression while the application is running."] = "Kann den Ausdruck nicht auswerten solange die Anwendung läuft.", -- src\editor\debugger.lua
["Can't open file '%s': %s"] = "Kann Datei '%s' nicht öffnen: %s", -- src\editor\findreplace.lua, src\editor\singleinstance.lua, src\editor\inspect.lua, src\editor\outline.lua
["Can't process auto-recovery record; invalid format: %s."] = "Auto-Wiederherstellen nicht möglich; ungültiges Format: %s.", -- src\editor\commands.lua
["Can't replace in read-only text."] = "Ersetzen in schreibgeschütztem Text nicht möglich.", -- src\editor\findreplace.lua
["Can't run the entry point script ('%s')."] = "Kann Script für Einsprungspunkt ('%s') nicht ausführen.", -- src\editor\debugger.lua
["Can't start debugger server at %s:%d: %s."] = "Kann Debugserver nicht starten (%s:%d): %s.", -- src\editor\debugger.lua
["Can't start debugging for '%s'."] = "Kann Debuggen nicht starten für '%s'.", -- src\editor\debugger.lua
["Can't start debugging session due to internal error '%s'."] = "Debugging kann nicht gestartet werden wegen internem Fehler '%s'.", -- src\editor\debugger.lua
["Can't start debugging without an opened file or with the current file not being saved ('%s')."] = "Debugging kann ohne geöffnete Datei nicht gestartet werden oder wenn die aktuelle Datei nicht gespeichert ist ('%s').", -- src\editor\debugger.lua
["Can't stop debugger server as it is not started."] = "Kann Debugserver nicht stoppen wenn er vorher nicht gestartet wurde.", -- src\editor\debugger.lua
["Cancelled by the user."] = "Durch Benutzer abgebrochen.", -- src\editor\findreplace.lua
["Choose a directory to map"] = "Bitte Verzeichnis wählen zum virtuellen Einbinden", -- src\editor\filetree.lua
["Choose a project directory"] = "Projektverzeichnis auswählen", -- src\editor\toolbar.lua, src\editor\menu_project.lua, src\editor\filetree.lua
["Choose a search directory"] = "Bitte Verzeichnis zum Durchsuchen auswählen.", -- src\editor\findreplace.lua
["Choose..."] = "Wählen...", -- src\editor\findreplace.lua, src\editor\menu_project.lua, src\editor\filetree.lua
["Clear Items"] = "Liste &löschen", -- src\editor\findreplace.lua, src\editor\menu_file.lua
["Clear items from this list"] = "Diese Liste löschen", -- src\editor\menu_file.lua
["Clear the output window before compiling or debugging"] = "Vor Kompilieren oder Debuggen das Ausgabefenster löschen", -- src\editor\menu_project.lua
["Close &Other Pages"] = "A&ndere Seiten schließen", -- src\editor\gui.lua
["Close A&ll Pages"] = "&Alle Seiten schließen", -- src\editor\gui.lua
["Close the current editor window"] = "Aktuelles Editorfenster schließen", -- src\editor\menu_file.lua
["Co&ntinue"] = "&Fortsetzen", -- src\editor\menu_project.lua
["Col: %d"] = "Spalte: %d", -- src\editor\editor.lua
["Command Line Parameters..."] = "Kommandozeilenparameter...", -- src\editor\menu_project.lua
["Command line parameters"] = "Kommandozeilenparameter", -- src\editor\menu_project.lua
["Comment or uncomment current or selected lines"] = "Ausgewählte bzw. aktive Zeile (un-)kommentieren", -- src\editor\menu_edit.lua
["Compilation error"] = "Fehler beim Kompilieren", -- src\editor\commands.lua, src\editor\debugger.lua
["Compilation successful; %.0f%% success rate (%d/%d)."] = "Kompilieren erfolgreich; Erfolgsquote von %.0f%% (%d/%d).", -- src\editor\commands.lua
["Compile the current file"] = "Aktuelle Datei kompilieren", -- src\editor\menu_project.lua
["Complete &Identifier"] = "&Bezeichner vervollständigen", -- src\editor\menu_edit.lua
["Complete the current identifier"] = " Aktuellen Bezeichner vervollständigen", -- src\editor\menu_edit.lua
["Consider removing backslash from escape sequence '%s'."] = "Möglicherweise muß `\' aus '%s' entfernt werden.", -- src\editor\commands.lua
["Copy Full Path"] = "Kopiere Pfadangabe", -- src\editor\gui.lua, src\editor\filetree.lua
["Copy selected text to clipboard"] = "Text in Zwischenablage kopieren", -- src\editor\menu_edit.lua
["Correct &Indentation"] = "E&inzug korrigieren", -- src\editor\menu_edit.lua
["Couldn't activate file '%s' for debugging; continuing without it."] = "Konnte Datei '%s' zwecks nicht Debugging aktivieren; fahre ohne die Datei fort.", -- src\editor\debugger.lua
["Create an empty document"] = "Leeres Dokument anlegen", -- src\editor\toolbar.lua, src\editor\menu_file.lua
["Cu&t"] = "A&usschneiden", -- src\editor\gui.lua, src\editor\editor.lua, src\editor\menu_edit.lua
["Cut selected text to clipboard"] = "Schneide ausgewählten Text in die Zwischenablage hinein", -- src\editor\menu_edit.lua
["Debugger server started at %s:%d."] = "Debugserver gestartet als %s:%d.", -- src\editor\debugger.lua
["Debugger server stopped at %s:%d."] = "Debugserver gestoppt als %s:%d.", -- src\editor\debugger.lua
["Debugging session completed (%s)."] = "Debugging Session beendet (%s).", -- src\editor\debugger.lua
["Debugging session started in '%s'."] = "Debugging Session gestartet '%s'.", -- src\editor\debugger.lua
["Debugging suspended at '%s:%s' (couldn't activate the file)."] = "Debugging angehalten bei '%s:%s' (konnte Datei nicht aktivieren).", -- src\editor\debugger.lua
["Detach &Process"] = "Prozeß abkoppeln", -- src\editor\menu_project.lua
["Disable Indexing For '%s'"] = "Indizieren ausschalten für '%s'", -- src\editor\outline.lua
["Do you want to delete '%s'?"] = "Soll '%s' gelöscht werden?", -- src\editor\filetree.lua
["Do you want to overwrite it?"] = "Überschreiben?", -- src\editor\commands.lua
["Do you want to reload it?"] = "Neu laden?", -- src\editor\editor.lua
["Do you want to save the changes to '%s'?"] = "Änderungen an '%s' speichern?", -- src\editor\commands.lua
["E&xit"] = "&Beenden", -- src\editor\menu_file.lua
["Enable Indexing"] = "Indizieren aktivieren", -- src\editor\outline.lua
["Enter Lua code and press Enter to run it."] = "Lua-Code eingeben und Enter drücken zum Ausführen.", -- src\editor\shellbox.lua
["Enter command line parameters (use Cancel to clear)"] = "Kommandozeilenparameter eingeben (Cancel zum löschen)", -- src\editor\menu_project.lua
["Enter replacement text"] = "Neuen Text eingeben", -- src\editor\editor.lua
["Error while loading API file: %s"] = "Fehler beim Laden von API-Datei: %s", -- src\editor\autocomplete.lua
["Error while loading configuration file: %s"] = "Fehler beim Laden von Konfigurationsdatei: %s", -- src\editor\style.lua
["Error while processing API file: %s"] = "Fehler beim Lesen von API-Datei: %s", -- src\editor\autocomplete.lua
["Error while processing configuration file: %s"] = "Fehler beim Lesen von Konfiguratonsdatei: %s", -- src\editor\style.lua
["Error"] = "Fehler", -- src\editor\commands.lua
["Evaluate In Console"] = "In Konsole auswerten", -- src\editor\editor.lua
["Execute the current project/file and keep updating the code to see immediate results"] = "Aktuelles Projekt/ aktuelle Datei ausführen und Quellcode ändern, um Ergebnisse in Echtzeit zu sehen", -- src\editor\menu_project.lua
["Execute the current project/file"] = "Aktuelles Projekt/ aktuelle Datei ausführen", -- src\editor\toolbar.lua, src\editor\menu_project.lua
["Execution error"] = "Fehler bei Ausführung", -- src\editor\debugger.lua
["Exit program"] = "Programm beenden", -- src\editor\menu_file.lua
["File '%s' has been modified on disk."] = "Datei '%s' wurde auf der Festplatte geändert.", -- src\editor\editor.lua
["File '%s' has more recent timestamp than restored '%s'; please review before saving."] = "Datei '%s' hat neueren Zeitstempel als wiederhergestellte Datei '%s'; bitte vor dem Speichern kontrollieren.", -- src\editor\commands.lua
["File '%s' is missing and can't be recovered."] = "Datei '%s' fehlt und kann nicht wiederhergestellt werden.", -- src\editor\commands.lua
["File '%s' no longer exists."] = "Datei '%s' existiert nicht mehr.", -- src\editor\menu_file.lua, src\editor\editor.lua
["File already exists."] = "Datei existiert bereits.", -- src\editor\commands.lua
["File history"] = "Dateiverlauf", -- src\editor\menu_file.lua
["Find &In Files"] = "Finde &in Dateien", -- src\editor\menu_search.lua
["Find &Next"] = "Finde &Nächste", -- src\editor\menu_search.lua
["Find &Previous"] = "Finde &Vorherige", -- src\editor\menu_search.lua
["Find and insert library function"] = "Suchen und Einfügen von Funktion aus Bibliothek", -- src\editor\menu_search.lua
["Find and replace text in files"] = "Finde und ersetze Text in Dateien", -- src\editor\menu_search.lua
["Find and replace text"] = "Finde und ersetze Text", -- src\editor\toolbar.lua, src\editor\menu_search.lua
["Find in files"] = "Finde in dateien", -- src\editor\toolbar.lua
["Find text in files"] = "Finde Text in Dateien", -- src\editor\menu_search.lua
["Find text"] = "Finde Text", -- src\editor\toolbar.lua, src\editor\menu_search.lua
["Find the earlier text occurence"] = "Finde vorheriges Auftreten des Textes", -- src\editor\menu_search.lua
["Find the next text occurrence"] = "Finde nächstes Auftreten des Textes", -- src\editor\menu_search.lua
["Find"] = "Finden", -- src\editor\toolbar.lua
["Fold or unfold all code folds"] = "Alle Stellen im Code ein-/ausklappen ", -- src\editor\menu_edit.lua
["Formatting page %d..."] = nil, -- src\editor\print.lua
["Found %d instance."] = {"Eine Instanz gefunden", "%d Instanzen gefunden."}, -- src\editor\findreplace.lua
["Found auto-recovery record and restored saved session."] = "Autowiederherstellen-Aufzeichnung gefunden und vorherige Sitzung wiederhergestellt.", -- src\editor\commands.lua
["Full &Screen"] = "&Vollbild", -- src\editor\menu_view.lua
["Go To Definition"] = "Gehe zu Definition", -- src\editor\editor.lua
["Go To File..."] = "Gehe zu Datei...", -- src\editor\menu_search.lua
["Go To Line..."] = "Gehe zu Zeile...", -- src\editor\menu_search.lua
["Go To Next Bookmark"] = "Zu nächstem Lesezeichen", -- src\editor\menu_edit.lua
["Go To Next Breakpoint"] = nil, -- src\editor\menu_project.lua
["Go To Previous Bookmark"] = "Zu vorherigem Lesezeichen", -- src\editor\menu_edit.lua
["Go To Previous Breakpoint"] = nil, -- src\editor\menu_project.lua
["Go To Symbol..."] = "Gehe zu Symbol...", -- src\editor\menu_search.lua
["Go to file"] = "Gehe zu Datei", -- src\editor\menu_search.lua
["Go to line"] = "Gehe zu Zeile", -- src\editor\menu_search.lua
["Go to symbol"] = "Gehe zu Symbol", -- src\editor\menu_search.lua
["Hide '.%s' Files"] = "Verstecke '.%s' Dateien", -- src\editor\filetree.lua
["INS"] = "INS", -- src\editor\editor.lua
["Ignore and don't index symbols from files in the selected directory"] = "Dateien im ausgewählten Verzeichnis ignorieren und keine Symbole indizieren.", -- src\editor\outline.lua
["Ignored error in debugger initialization code: %s."] = "Ignorierter Fehler im Debugger-Init-Code: %s.", -- src\editor\debugger.lua
["Indexing %d files: '%s'..."] = "%d Dateien indizieren: '%s'...", -- src\editor\outline.lua
["Indexing completed."] = "Indizierung abgeschlossen.", -- src\editor\outline.lua
["Insert Library Function..."] = "Füge Funktion aus Bibliothek ein", -- src\editor\menu_search.lua
["Known Files"] = "Bekannte Dateien", -- src\editor\commands.lua
["Ln: %d"] = "Zeile: %d", -- src\editor\editor.lua
["Local console"] = "Lokale Konsole", -- src\editor\gui.lua, src\editor\shellbox.lua
["Lua &Interpreter"] = "&Lua Interpreter", -- src\editor\menu_project.lua
["Map Directory..."] = "Verzeichnis virtuell einbinden...", -- src\editor\filetree.lua
["Mapped remote request for '%s' to '%s'."] = "Fern-Aufforderung für '%s' auf '%s' abgebildet.", -- src\editor\debugger.lua
["Markers Window"] = nil, -- src\editor\menu_view.lua
["Markers"] = nil, -- src\editor\markers.lua
["Match case"] = "Groß-/Kleinschreibung", -- src\editor\toolbar.lua
["Match whole word"] = "Ganzes Wort", -- src\editor\toolbar.lua
["Mixed end-of-line encodings detected."] = "Gemischte End-of-Line Kodierung entdeckt.", -- src\editor\commands.lua
["Navigate"] = "Navigieren", -- src\editor\menu_search.lua
["New &File"] = "Neue &Datei", -- src\editor\filetree.lua
["OVR"] = "OVR", -- src\editor\editor.lua
["Open With Default Program"] = "Mit Standardanwendung öffnen", -- src\editor\filetree.lua
["Open an existing document"] = "Öffne existierendes Dokument", -- src\editor\toolbar.lua, src\editor\menu_file.lua
["Open file"] = "Öffne Datei", -- src\editor\commands.lua
["Outline Window"] = "Übersichtsfenster", -- src\editor\menu_view.lua
["Outline"] = "Übersicht", -- src\editor\outline.lua
["Output (running)"] = "Ausgabe (ausgeführt)", -- src\editor\debugger.lua, src\editor\output.lua
["Output (suspended)"] = "Ausgabe (angehalten)", -- src\editor\debugger.lua
["Output"] = "Ausgabe", -- src\editor\debugger.lua, src\editor\output.lua, src\editor\gui.lua, src\editor\settings.lua
["Page Setup..."] = nil, -- src\editor\print.lua
["Paste text from the clipboard"] = "Text aus Zwischenablage einfügen", -- src\editor\menu_edit.lua
["Preferences"] = "Einstellungen", -- src\editor\menu_edit.lua
["Prepend '!' to force local execution."] = "'!' voranstellen um lokale Ausführung zu erzwingen.", -- src\editor\shellbox.lua
["Prepend '=' to show complex values on multiple lines."] = "'=' voranstellen, um komplexe Ausdrücke auf mehrere Zeilen zu verteilen.", -- src\editor\shellbox.lua
["Press cancel to abort."] = "Abbrechen Drücken zum Beenden.", -- src\editor\commands.lua
["Print the current document"] = nil, -- src\editor\print.lua
["Program '%s' started in '%s' (pid: %d)."] = "Programm '%s' gestartet in '%s' (pid : %d).", -- src\editor\output.lua
["Program can't start because conflicting process is running as '%s'."] = "Programm kann nicht starten, da blockierender Prozeß als '%s' läuft.", -- src\editor\output.lua
["Program completed in %.2f seconds (pid: %d)."] = "Programm beendet nach %.2f Sekunden (pid : %d).", -- src\editor\output.lua
["Program starting as '%s'."] = "Programm gestartet als '%s'.", -- src\editor\output.lua
["Program stopped (pid: %d)."] = "Programm gestoppt (pid: %d).", -- src\editor\debugger.lua
["Program unable to run as '%s'."] = "Programm kann nicht als '%s' laufen.", -- src\editor\output.lua
["Project Directory"] = "&Projektverzeichnis", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Project history"] = "Liste bisheriger Projekte", -- src\editor\menu_file.lua
["Project"] = "Projekt", -- src\editor\filetree.lua
["Project/&FileTree Window"] = "&Projekt/Datei Fenster", -- src\editor\menu_view.lua
["Provide command line parameters"] = "Kommandozeilenparameter angeben", -- src\editor\menu_project.lua
["Queued %d files to index."] = "%d Dateien zum Indizieren vorgemerkt.", -- src\editor\menu_search.lua
["R/O"] = "R/O", -- src\editor\editor.lua
["R/W"] = "R/W", -- src\editor\editor.lua
["Re&place In Files"] = "Ersetze in &Dateien", -- src\editor\menu_search.lua
["Re-indent selected lines"] = "Ausgewählte Zeilen neu einrücken", -- src\editor\menu_edit.lua
["Reached end of selection and wrapped around."] = nil, -- src\editor\findreplace.lua
["Reached end of text and wrapped around."] = "Ende des Textes erreicht, setze am Beginn fort.", -- src\editor\findreplace.lua
["Recent Files"] = "Letzte Dateien", -- src\editor\menu_file.lua
["Recent Projects"] = "Letzte Projekte", -- src\editor\menu_file.lua
["Redo last edit undone"] = "Stelle letzte rückgängig gemachte Bearbeitung wieder her", -- src\editor\menu_edit.lua
["Refresh Index"] = "Index erneuern", -- src\editor\outline.lua
["Refresh indexed symbols from files in the selected directory"] = "Erneuere indizierte Symbole in Dateien des ausgewählten Verzeichnisses", -- src\editor\outline.lua
["Refresh"] = "Aktualisieren", -- src\editor\filetree.lua
["Refused a request to start a new debugging session as there is one in progress already."] = "Starten einer neuen Debuggingsession abgelehnt, da bereits eine läuft.", -- src\editor\debugger.lua
["Regular expression"] = "Regulärer Ausdruck", -- src\editor\toolbar.lua
["Remote console"] = "Fernsteuerungs-Konsole", -- src\editor\shellbox.lua
["Rename All Instances"] = "Umbenennen aller Instanzen", -- src\editor\editor.lua
["Replace All Selections"] = "Alle Auswahlen ersetzen", -- src\editor\editor.lua
["Replace all"] = "Alles ersetzen", -- src\editor\toolbar.lua
["Replace next instance"] = "Nächste Instanz ersetzen", -- src\editor\toolbar.lua
["Replaced %d instance."] = {"Eine Instanz ersetzt.", "%d Instanzen ersetzt."}, -- src\editor\findreplace.lua
["Replaced an invalid UTF8 character with %s."] = "Unbekanntes UTF8-Symbol ersetzt mit %s.", -- src\editor\commands.lua
["Reset to default layout"] = "Standard-Layout wiederherstellen", -- src\editor\menu_view.lua
["Run As Scratchpad"] = "Als &Entwurf starten", -- src\editor\menu_project.lua
["Run To Cursor"] = "Stoppe Ausführung an Cursorposition", -- src\editor\menu_project.lua, src\editor\editor.lua
["Run as Scratchpad"] = "Als &Entwurf starten", -- src\editor\toolbar.lua
["Run to cursor"] = "Stoppe Ausführung an Cursorposition", -- src\editor\toolbar.lua, src\editor\menu_project.lua
["S&top Debugging"] = "Debugging a&nhalten", -- src\editor\menu_project.lua
["S&top Process"] = "Prozeß &anhalten", -- src\editor\menu_project.lua
["Save &As..."] = "S&peichern als...", -- src\editor\gui.lua, src\editor\menu_file.lua
["Save A&ll"] = "&Alle Speichern", -- src\editor\menu_file.lua
["Save Changes?"] = "Änderungen speichern?", -- src\editor\commands.lua
["Save all open documents"] = "Alle offenen Dokumente speichern", -- src\editor\toolbar.lua, src\editor\menu_file.lua
["Save file as"] = "Datei speichern als", -- src\editor\commands.lua
["Save file?"] = "Datei speichern?", -- src\editor\commands.lua
["Save the current document to a file with a new name"] = "Aktuelles Dokument unter neuem Namen speichern", -- src\editor\menu_file.lua
["Save the current document"] = "Aktuelles Dokument speichern", -- src\editor\toolbar.lua, src\editor\menu_file.lua
["Saved auto-recover at %s."] = "%s Autowiederherstellen gespeichert.", -- src\editor\commands.lua
["Scratchpad error"] = "Fehler im Entwurf", -- src\editor\debugger.lua
["Search direction"] = "Suchrichtung", -- src\editor\toolbar.lua
["Search in selection"] = nil, -- src\editor\toolbar.lua
["Search in subdirectories"] = "Suche in Unterverzeichnissen", -- src\editor\toolbar.lua
["Searching for '%s'."] = "Suche nach '%s'.", -- src\editor\findreplace.lua
["Sel: %d/%d"] = "Ausgew.: %d/%d", -- src\editor\editor.lua
["Select &All"] = "&Alles Auswählen", -- src\editor\gui.lua, src\editor\editor.lua, src\editor\menu_edit.lua
["Select And Find Next"] = "Auswählen und nächstes finden", -- src\editor\menu_search.lua
["Select And Find Previous"] = "Auswählen und vorheriges finden", -- src\editor\menu_search.lua
["Select all text in the editor"] = "Kompletten Text im Editor auswählen", -- src\editor\menu_edit.lua
["Select the word under cursor and find its next occurrence"] = "Wort unter dem Cursor auswählen und nächstes Auftauchen finden", -- src\editor\menu_search.lua
["Select the word under cursor and find its previous occurrence"] = "Wort unter dem Cursor auswählen und vorheriges Auftauchen finden", -- src\editor\menu_search.lua
["Set As Start File"] = "Als Startdatei definieren.", -- src\editor\filetree.lua
["Set From Current File"] = "Anhand der aktuellen Datei festlegen", -- src\editor\menu_project.lua
["Set To Project Directory"] = "Setze als Projektverzeichnis", -- src\editor\findreplace.lua
["Set To Selected Directory"] = nil, -- src\editor\filetree.lua
["Set project directory from current file"] = "Lege Projektverzeichnis anhand der aktuellen Datei fest", -- src\editor\toolbar.lua, src\editor\menu_project.lua
["Set project directory to the selected one"] = nil, -- src\editor\filetree.lua
["Set search directory"] = "Setze Suchverzeichnis", -- src\editor\toolbar.lua
["Set the interpreter to be used"] = "Wähle zu benutzenden Interpreter aus", -- src\editor\menu_project.lua
["Set the project directory to be used"] = "Lege zu benutzendes Projektverzeichnis fest", -- src\editor\menu_project.lua, src\editor\filetree.lua
["Settings: System"] = "Einstellungen: System", -- src\editor\menu_edit.lua
["Settings: User"] = "Einstellungen: Nutzer", -- src\editor\menu_edit.lua
["Show &Tooltip"] = "&Tooltip zeigen", -- src\editor\menu_edit.lua
["Show All Files"] = "Zeige alle Dateien", -- src\editor\filetree.lua
["Show Hidden Files"] = "Zeige versteckte Dateien", -- src\editor\filetree.lua
["Show Location"] = "Ordner öffnen", -- src\editor\gui.lua, src\editor\filetree.lua
["Show all files"] = "Zeige alle Dateien", -- src\editor\filetree.lua
["Show context"] = "Zeige Kontext", -- src\editor\toolbar.lua
["Show files previously hidden"] = "Zeige Dateien die zuvor versteckt wurden", -- src\editor\filetree.lua
["Show multiple result windows"] = "Zeige mehrere Ergebnisfenster", -- src\editor\toolbar.lua
["Show tooltip for current position; place cursor after opening bracket of function"] = "Zeige Tooltip für aktuelle Position; setze Cursor hinter die öffnende Klammer der Funktion", -- src\editor\menu_edit.lua
["Show/Hide the status bar"] = "Statuszeile zeigen/verstecken", -- src\editor\menu_view.lua
["Show/Hide the toolbar"] = "Werkzeugleiste zeigen/verstecken", -- src\editor\menu_view.lua
["Sort By Name"] = "Sortiere nach Namen", -- src\editor\outline.lua
["Sort selected lines"] = "Ausgewählte Zeilen sortieren", -- src\editor\menu_edit.lua
["Source"] = "Source", -- src\editor\menu_edit.lua
["Stack"] = "Stack", -- src\editor\debugger.lua
["Start &Debugging"] = "&Debugging starten", -- src\editor\menu_project.lua
["Start or continue debugging"] = "Debuggen starten/fortsetzen", -- src\editor\toolbar.lua, src\editor\menu_project.lua
["Step &Into"] = "Schritt h&inein", -- src\editor\menu_project.lua
["Step &Over"] = "&Überspringen", -- src\editor\menu_project.lua
["Step O&ut"] = "Schritt &raus", -- src\editor\menu_project.lua
["Step into"] = "Schritt hinein", -- src\editor\toolbar.lua, src\editor\menu_project.lua
["Step out of the current function"] = "Schritt aus der aktuellen Funktion heraus", -- src\editor\toolbar.lua, src\editor\menu_project.lua
["Step over"] = "Überspringen", -- src\editor\toolbar.lua, src\editor\menu_project.lua
["Stop debugging and continue running the process"] = "Beende debuggen und setze den Prozeß fort", -- src\editor\toolbar.lua, src\editor\menu_project.lua
["Stop the currently running process"] = "Aktuell laufenden Prozeß stoppen", -- src\editor\toolbar.lua, src\editor\menu_project.lua
["Switch to or from full screen mode"] = "Vollbild an/aus", -- src\editor\menu_view.lua
["Symbol Index"] = "Symbolindex", -- src\editor\outline.lua
["Text not found."] = "Text nicht gefunden.", -- src\editor\findreplace.lua
["The API file must be located in a subdirectory of the API directory."] = "Die API-Datei muß sich in einem Unterverzeichnis des API-Verzeichnisses befinden.", -- src\editor\autocomplete.lua
["Toggle Bookmark"] = "Lesezeichen setzen/löschen", -- src\editor\markers.lua, src\editor\menu_edit.lua
["Toggle Breakpoint"] = "Haltepunkt an/aus", -- src\editor\markers.lua, src\editor\menu_project.lua
["Toggle bookmark"] = "Lesezeichen setzen/löschen", -- src\editor\toolbar.lua, src\editor\menu_edit.lua, src\editor\markers.lua
["Toggle breakpoint"] = "Haltepunkt an/aus", -- src\editor\markers.lua, src\editor\toolbar.lua
["Tr&ace"] = "Ablauf &verfolgen", -- src\editor\menu_project.lua
["Trace execution showing each executed line"] = "Ablaufverfolgung zeigt jede ausgeführte Zeile an", -- src\editor\menu_project.lua
["Unable to create directory '%s'."] = "Kann kein Verzeichnis '%s' erstellen.", -- src\editor\filetree.lua
["Unable to create file '%s'."] = "Kann Datei '%s' nicht erstellen.", -- src\editor\filetree.lua
["Unable to delete directory '%s': %s"] = "Scheitern beim Löschen von Verzeichnis '%s': %s", -- src\editor\filetree.lua
["Unable to delete file '%s': %s"] = "Kann Datei '%s' nicht löschen: %s", -- src\editor\filetree.lua
["Unable to load file '%s'."] = "Scheitern beim Laden von Datei '%s'.", -- src\editor\commands.lua
["Unable to rename file '%s'."] = "Scheitern beim umbenennen von Datei '%s'.", -- src\editor\filetree.lua
["Unable to save file '%s': %s"] = "Scheitern beim Speichern von Datei '%s' : %s", -- src\editor\commands.lua
["Unable to stop program (pid: %d), code %d."] = "Scheitern beim Stoppen des Prozesses (pid : %d), code %d.", -- src\editor\debugger.lua
["Undo last edit"] = "Letzte Änderung rückgängig machen", -- src\editor\menu_edit.lua
["Unmap Directory"] = "virtuelle Einbindung beenden", -- src\editor\filetree.lua
["Unset '%s' As Start File"] = "'%s' ist nicht mehr Startdatei", -- src\editor\filetree.lua
["Updated %d file."] = {"Eine Datei aktualisiert", "%d Dateien aktualisiert."}, -- src\editor\findreplace.lua
["Updating symbol index and settings..."] = "Aktualisiere Symbolindex und Einstellungen...", -- src\editor\outline.lua
["Use %s to close."] = "%s zum Schließen.", -- src\editor\findreplace.lua
["Use '%s' to see full description."] = "'%s' für eine komplette Beschreibung.", -- src\editor\editor.lua
["Use '%s' to show line endings and '%s' to convert them."] = "'%s' um Zeilenende-Codes zu sehen, und '%s' um sie zu konvertieren.", -- src\editor\commands.lua
["Use 'clear' to clear the shell output and the history."] = "'clear' um Ausgabefenster und Verlauf zu löschen.", -- src\editor\shellbox.lua
["Use Shift-Enter for multiline code."] = "<Umsch-Eingabetaste> für Code in mehreren Zeilen.", -- src\editor\shellbox.lua
["View the markers window"] = nil, -- src\editor\menu_view.lua
["View the outline window"] = "Übersichtsfenster ansehen", -- src\editor\menu_view.lua
["View the output/console window"] = "Ausgabe-/Konsolenfenster ansehen", -- src\editor\menu_view.lua
["View the project/filetree window"] = "Projekt-/Dateifenster ansehen", -- src\editor\menu_view.lua
["View the stack window"] = "Stapel/Stack-Fenster ansehen", -- src\editor\toolbar.lua, src\editor\menu_view.lua
["View the watch window"] = "Fenster für Beobachtungspunkte ansehen", -- src\editor\toolbar.lua, src\editor\menu_view.lua
["Watch"] = "Beobachtungspunkte", -- src\editor\debugger.lua
["Welcome to the interactive Lua interpreter."] = "Willkommen zum interaktiven Lua-Interpretr!", -- src\editor\shellbox.lua
["Wrap around"] = "Am Anfang fortsetzen", -- src\editor\toolbar.lua
["You must save the program first."] = "Erst das Programm speichern.", -- src\editor\commands.lua
["Zoom In"] = "Hineinzoomen", -- src\editor\menu_view.lua
["Zoom Out"] = "Herauszoomen", -- src\editor\menu_view.lua
["Zoom to 100%"] = "Zoom zurücksetzen (100%)", -- src\editor\menu_view.lua
["Zoom"] = "Zoom", -- src\editor\menu_view.lua
["on line %d"] = "in Zeile %d", -- src\editor\debugger.lua, src\editor\editor.lua, src\editor\commands.lua
["traced %d instruction"] = {"%d Anweisung verfolgt", "%d Anweisungen verfolgt"}, -- src\editor\debugger.lua
["unknown error"] = "Unbekannter Fehler", -- src\editor\debugger.lua
}

View File

@ -1,7 +0,0 @@
return {
[0] = function(c) return c == 1 and 1 or 2 end, -- plural
["traced %d instruction"] = {"traced %d instruction", "traced %d instructions"}, -- src\editor\debugger.lua
["Found %d instance."] = {"Found %d instance.", "Found %d instances."}, -- src\editor\findreplace.lua
["Replaced %d instance."] = {"Replaced %d instance.", "Replaced %d instances."}, -- src\editor\findreplace.lua
["Updated %d file."] = {"Updated %d file.", "Updated %d files."}, -- src\editor\findreplace.lua
}

View File

@ -1,28 +0,0 @@
--[[
1. Pick a color scheme by clicking on its name:
- [Tomorrow](macro:inline(ApplyStyleConfig('cfg/tomorrow.lua','Tomorrow'); local c = ide.config; c.stylesoutshell = c.styles; c.styles.auxwindow = c.styles.text; ReApplySpecAndStyles()))
- [TomorrowContrast](macro:inline(ApplyStyleConfig('cfg/tomorrow.lua','TomorrowContrast'); local c = ide.config; c.stylesoutshell = c.styles; c.styles.auxwindow = c.styles.text; ReApplySpecAndStyles()))
- [TomorrowNight](macro:inline(ApplyStyleConfig('cfg/tomorrow.lua','TomorrowNight'); local c = ide.config; c.stylesoutshell = c.styles; c.styles.auxwindow = c.styles.text; ReApplySpecAndStyles()))
- [TomorrowNightBlue](macro:inline(ApplyStyleConfig('cfg/tomorrow.lua','TomorrowNightBlue'); local c = ide.config; c.stylesoutshell = c.styles; c.styles.auxwindow = c.styles.text; ReApplySpecAndStyles()))
- [TomorrowNightBright](macro:inline(ApplyStyleConfig('cfg/tomorrow.lua','TomorrowNightBright'); local c = ide.config; c.stylesoutshell = c.styles; c.styles.auxwindow = c.styles.text; ReApplySpecAndStyles()))
- [TomorrowNightEighties](macro:inline(ApplyStyleConfig('cfg/tomorrow.lua','TomorrowNightEighties'); local c = ide.config; c.stylesoutshell = c.styles; c.styles.auxwindow = c.styles.text; ReApplySpecAndStyles()))
- [Zenburn](macro:inline(ApplyStyleConfig('cfg/tomorrow.lua','Zenburn'); local c = ide.config; c.stylesoutshell = c.styles; c.styles.auxwindow = c.styles.text; ReApplySpecAndStyles()))
- [Monokai](macro:inline(ApplyStyleConfig('cfg/tomorrow.lua','Monokai'); local c = ide.config; c.stylesoutshell = c.styles; c.styles.auxwindow = c.styles.text; ReApplySpecAndStyles()))
- [Molokai](macro:inline(ApplyStyleConfig('cfg/tomorrow.lua','Molokai'); local c = ide.config; c.stylesoutshell = c.styles; c.styles.auxwindow = c.styles.text; ReApplySpecAndStyles()))
- [SolarizedDark](macro:inline(ApplyStyleConfig('cfg/tomorrow.lua','SolarizedDark'); local c = ide.config; c.stylesoutshell = c.styles; c.styles.auxwindow = c.styles.text; ReApplySpecAndStyles()))
- [SolarizedLight](macro:inline(ApplyStyleConfig('cfg/tomorrow.lua','SolarizedLight'); local c = ide.config; c.stylesoutshell = c.styles; c.styles.auxwindow = c.styles.text; ReApplySpecAndStyles()))
- [Notepad++](macro:inline(ApplyStyleConfig('cfg/tomorrow.lua','NotepadPlusPlus'); local c = ide.config; c.stylesoutshell = c.styles; c.styles.auxwindow = c.styles.text; ReApplySpecAndStyles()))
- [SciTeLuaIDE](macro:inline(ApplyStyleConfig('cfg/tomorrow.lua','SciTeLuaIDE'); local c = ide.config; c.stylesoutshell = c.styles; c.styles.auxwindow = c.styles.text; ReApplySpecAndStyles()))
- [ZeroBrane Studio](macro:inline(ide.config.styles = StylesGetDefault(); local c = ide.config; c.stylesoutshell = c.styles; c.styles.auxwindow = {}; ReApplySpecAndStyles()))
2. Add the following code with the scheme you selected to `cfg/user.lua`.
--]]
styles = loadfile('cfg/tomorrow.lua')('TomorrowNightBlue')
stylesoutshell = styles -- apply the same scheme to Output/Console windows
styles.auxwindow = styles.text -- apply text colors to auxiliary windows
styles.calltip = styles.text -- apply text colors to tooltips
-- code example
if false and true then func(1, 2, 3) end

View File

@ -1,311 +0,0 @@
-- This is a file that sets color scheme based on Tomorrow format.
-- Copyright 2011-14 Paul Kulchenko, ZeroBrane LLC
-- Tomorrow colors from https://github.com/chriskempson/tomorrow-theme
-- Zenburn colors from https://github.com/jnurmine/Zenburn/blob/master/colors/zenburn.vim (contributed by Srdjan Marković)
-- Monokai colors from http://www.monokai.nl/blog/2006/07/15/textmate-color-theme/
-- Molokai colors based on https://github.com/tomasr/molokai/blob/master/colors/molokai.vim
-- Solarized colors from https://github.com/altercation/vim-colors-solarized
local theme = ...
local function h2d(n) return 0+('0x'..n) end
local function H(c, bg) c = c:gsub('#','')
-- since alpha is not implemented, convert RGBA to RGB
-- assuming 0 is transparent and 255 is opaque
-- based on http://stackoverflow.com/a/2645218/1442917
local bg = bg and H(bg) or {255, 255, 255}
local a = #c > 6 and h2d(c:sub(7,8))/255 or 1
local r, g, b = h2d(c:sub(1,2)), h2d(c:sub(3,4)), h2d(c:sub(5,6))
return {
math.min(255, math.floor((1-a)*bg[1]+a*r)),
math.min(255, math.floor((1-a)*bg[2]+a*g)),
math.min(255, math.floor((1-a)*bg[3]+a*b))}
end
local colors = {
Tomorrow = {
Background = H'ffffff',
CurrentLine = H'efefef',
Selection = H'd6d6d6',
Foreground = H'4d4d4c',
Comment = H'8e908c',
Red = H'c82829',
Orange = H'f5871f',
Yellow = H'eab700',
Green = H'718c00',
Aqua = H'3e999f',
Blue = H'4271ae',
Purple = H'8959a8',
},
TomorrowContrast = { -- contributed by Sergey Lerg
Background = H'f7f7f7',
CurrentLine = H'efefef',
Selection = H'd6d6d6',
Foreground = H'202020',
Comment = H'8e908c',
Red = H'4669ff', --numbers
Orange = H'f5871f',
Yellow = H'eab700',
Green = H'108010', --strings
Aqua = H'4060b0', --built in functions
Blue = H'101080', --keywords
Purple = H'a01090',
},
TomorrowNight = {
Background = H'1d1f21',
CurrentLine = H'282a2e',
Selection = H'373b41',
Foreground = H'c5c8c6',
Comment = H'969896',
Red = H'cc6666',
Orange = H'de935f',
Yellow = H'f0c674',
Green = H'b5bd68',
Aqua = H'8abeb7',
Blue = H'81a2be',
Purple = H'b294bb',
},
TomorrowNightEighties = {
Background = H'2d2d2d',
CurrentLine = H'393939',
Selection = H'515151',
Foreground = H'cccccc',
Comment = H'999999',
Red = H'f2777a',
Orange = H'f99157',
Yellow = H'ffcc66',
Green = H'99cc99',
Aqua = H'66cccc',
Blue = H'6699cc',
Purple = H'cc99cc',
},
TomorrowNightBlue = {
Background = H'002451',
CurrentLine = H'00346e',
Selection = H'003f8e',
Foreground = H'ffffff',
Comment = H'7285b7',
Red = H'ff9da4',
Orange = H'ffc58f',
Yellow = H'ffeead',
Green = H'd1f1a9',
Aqua = H'99ffff',
Blue = H'bbdaff',
Purple = H'ebbbff',
},
TomorrowNightBright = {
Background = H'000000',
CurrentLine = H'2a2a2a',
Selection = H'424242',
Foreground = H'eaeaea',
Comment = H'969896',
Red = H'd54e53',
Orange = H'e78c45',
Yellow = H'e7c547',
Green = H'b9ca4a',
Aqua = H'70c0b1',
Blue = H'7aa6da',
Purple = H'c397d8',
},
Zenburn = {
Background = H'3f3f3f',
CurrentLine = H'363636',
Selection = H'1f1f1f',
Foreground = H'dcdccc',
Comment = H'7f9f7f',
Red = H'8cd0d3',
Orange = H'dfaf8f',
Yellow = H'e3ceab',
Green = H'cc9393',
Aqua = H'dca3a3',
Blue = H'f0dfaf',
Purple = H'efef8f',
},
Monokai = {
Background = H'272822',
CurrentLine = H'2D2F29',
Selection = H'49483E',
Foreground = H'F8F8F2',
Comment = H'75715E',
Red = H'AE81FF',
Orange = H'AE81FF',
Yellow = H'F8F8F2',
Green = H'E6DB74',
Aqua = H'66D9EF',
Blue = H'F92672',
Purple = H'A6E22E',
},
Molokai = {
Background = H'1B1D1E',
CurrentLine = H'293739',
Selection = H'49483E',
Foreground = H'F8F8F2',
Comment = H'7E8E91',
Red = H'AE81FF',
Orange = H'AE81FF',
Yellow = H'F8F8F2',
Green = H'E6DB74',
Aqua = H'66D9EF',
Blue = H'F92672',
Purple = H'A6E22E',
},
SolarizedDark = {
Background = H'042029',
CurrentLine = H'0A2933',
Selection = H'073642',
Foreground = H'839496',
Comment = H'586E75',
Red = H'D33682',
Orange = H'B58900',
Yellow = H'839496',
Green = H'2AA198',
Aqua = H'839496',
Blue = H'859900',
Purple = H'268BD2',
},
SolarizedLight = {
Background = H'FDF6E3',
CurrentLine = H'EEE8D5',
Selection = H'E0E0D0',
Foreground = H'586E75',
Comment = H'93A1A1',
Red = H'D33682',
Orange = H'B58900',
Yellow = H'586E75',
Green = H'2AA198',
Aqua = H'586E75',
Blue = H'859900',
Purple = H'268BD2',
},
NotepadPlusPlus = { -- contributed by Florian (https://github.com/SiENcE)
Background = H'FFFFFF',
CurrentLine = H'E9E2FF',
Selection = H'C0C0C0',
Foreground = H'000000',
Comment = H'008000',
Red = H'FF6900',
Orange = H'FF0000',
Yellow = H'FF4E00',
Green = H'808080',
Aqua = H'000080',
Blue = H'2123FF',
Purple = H'8000FF',
},
SciTeLuaIDE = { -- contributed by Jayanth Acharya
Background = H'1B1D1E',
CurrentLine = H'293739',
Selection = H'49483E',
Foreground = H'F8F8F2', -- super-light-gray (everything else)
Comment = H'00FF00', -- bright green (comments)
Red = H'F92672', -- purple (numbers)
Orange = H'AE81FF', -- lavendar?? (numbers)
Yellow = H'F8F8F2', -- light-gray
Green = H'FF8000', -- amber (string literal)
Aqua = H'808080', -- gray (operators, separators etc.)
Blue = H'0066FF', -- semi-blue (keywords)
Purple = H'A6E22E', -- light-grass-green
},
}
-- add more of the specified color (keeping all in 0-255 range)
local mixer = function(c, n, more)
if not c or #c == 0 then return c end
local c = {c[1], c[2], c[3]} -- create a copy, so it can be modified
c[n] = c[n] + more
local excess = c[n] - 255
if excess > 0 then
for clr = 1, 3 do
c[clr] = n == clr and 255 or c[clr] > excess and c[clr] - excess or 0
end
end
return c
end
local C = colors[theme] or colors.Tomorrow
return {
-- wxstc.wxSTC_LUA_DEFAULT
lexerdef = {fg = C.Foreground},
-- wxstc.wxSTC_LUA_COMMENT, wxstc.wxSTC_LUA_COMMENTLINE, wxstc.wxSTC_LUA_COMMENTDOC
comment = {fg = C.Comment, fill = true},
-- wxstc.wxSTC_LUA_STRING, wxstc.wxSTC_LUA_CHARACTER, wxstc.wxSTC_LUA_LITERALSTRING
stringtxt = {fg = C.Green},
-- wxstc.wxSTC_LUA_STRINGEOL
stringeol = {fg = C.Green, fill = true},
-- wxstc.wxSTC_LUA_PREPROCESSOR
preprocessor = {fg = C.Orange},
-- wxstc.wxSTC_LUA_OPERATOR
operator = {fg = C.Aqua},
-- wxstc.wxSTC_LUA_NUMBER
number = {fg = C.Red},
-- wxstc.wxSTC_LUA_WORD, wxstc.wxSTC_LUA_WORD2-8
keywords0 = {fg = C.Blue, b = true},
keywords1 = {fg = C.Aqua, b = false},
keywords2 = {fg = C.Aqua, b = true},
keywords3 = {fg = C.Purple, b = false},
keywords4 = {fg = C.Purple, b = false},
keywords5 = {fg = C.Purple, b = false},
keywords6 = {fg = C.Purple, b = false},
keywords7 = {fg = C.Purple, b = false},
-- common (inherit fg/bg from text)
-- wxstc.wxSTC_LUA_IDENTIFIER
text = {fg = C.Foreground, bg = C.Background},
linenumber = {fg = C.Comment},
bracematch = {fg = C.Orange, b = true},
bracemiss = {fg = C.Red, b = true},
ctrlchar = {fg = C.Yellow},
indent = {fg = C.Comment},
calltip = nil,
-- common special (need custom fg & bg)
sel = {bg = C.Selection},
caret = {fg = C.Foreground},
caretlinebg = {bg = C.CurrentLine},
fold = {fg = C.Comment, bg = C.Background, sel = mixer(C.Comment, 1, 96)},
whitespace = {fg = C.Comment, bg = C.Background},
edge = {},
indicator = {
fncall = {fg = C.Purple, st = wxstc.wxSTC_INDIC_HIDDEN},
--[[ other possible values are:
wxSTC_INDIC_PLAIN Single-line underline
wxSTC_INDIC_SQUIGGLE Squiggly underline
wxSTC_INDIC_TT Line of small T-shapes
wxSTC_INDIC_DIAGONAL Diagonal hatching
wxSTC_INDIC_STRIKE Strike-out
wxSTC_INDIC_BOX Box
wxSTC_INDIC_ROUNDBOX Rounded Box
--]]
-- these indicators have all different default styles
varlocal = {fg = C.Foreground},
varglobal = {fg = C.Foreground},
varmasked = {fg = C.Foreground},
varmasking = {fg = C.Foreground},
},
-- markup
['['] = {hs = mixer(C.Comment, 3, 64)},
['|'] = {fg = mixer(mixer(C.Comment, 1, 64), 3, 64)},
-- markers
marker = {
message = {bg = C.Selection},
output = {bg = C.CurrentLine},
prompt = {fg = C.Foreground, bg = C.Background},
error = {bg = mixer(C.Background, 1, 32)},
},
}
--[[
---- Solarized license ----
Copyright (c) 2011 Ethan Schoonover
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--]]

View File

@ -1,170 +0,0 @@
--[[-- This file shows examples of settings you can adjust.
Configuration files with preferences are loaded in the following order:
1. cfg/user.lua (system-wide configuration)
2. HOME/.zbstudio/user.lua (per-user configuration)
3. -cfg <lua code fragment|filename> (command line configuration)
See [configuration](http://studio.zerobrane.com/doc-configuration.html) page for information about location of configuration files.
--]]--
-- to modify loaded configuration for recognized extensions for lua files
-- (no longer needed in v1.21+) local G = ... -- this now points to the global environment
local luaspec = ide.specs.lua
luaspec.exts[#luaspec.exts+1] = "luaz"
luaspec.keywords[1] = luaspec.keywords[1] .. ' foo'
-- to modify a key mapping; see the full list of IDs in src/editor/keymap.lua
-- starting from v0.95, ID.<menuid> can be used instead of G.ID_<menuid>
keymap[ID.STARTDEBUG] = "Ctrl-Shift-D"
-- to change font size to 12
editor.fontsize = 12 -- this is mapped to ide.config.editor.fontsize
editor.fontname = "Courier New"
filehistorylength = 20 -- this is mapped to ide.config.filehistorylength
-- to specify full path to love2d *executable*; this is only needed
-- if the game folder and the executable are NOT in the same folder.
path.love2d = 'd:/lua/love/love'
-- to specify full path to moai *executable* if it's not in one of PATH folders
path.moai = 'd:/lua/moai/moai'
-- Moai config.lua file is searched in the following places: (1) MOAI_CONFIG,
-- (2) project directory (if set) or folder with the current file,
-- (3) folder with the moai executable.
-- to specify full path to gideros *executable* if it's not in one of PATH folders
path.gideros = 'd:/Program Files/Gideros/GiderosPlayer.exe'
-- to specify full path to corona *executable* if it's not in one of PATH folders
path.corona = 'd:/path/to/Corona SDK/Corona Simulator.exe'
-- to specify full path to lua interpreter if you need to use your own version
path.lua = 'd:/lua/lua'
-- to specify full path to GSL-shell *executable* if it's not in one of PATH folders
path.gslshell = [[D:\Lua\gsl-shell\gsl-shell.exe]]
-- to provide output filter for those engines that support redirecting
-- of "print" output to the IDE (like Corona SDK or Gideros)
debugger.outputfilter = function(m) return #m < 124 and m or m:sub(1,120).."...\n" end
-- to fix an issue with 0d0d0a line endings in MOAI examples,
-- which may negatively affect breakpoints during debugging
editor.iofilter = "0d0d0aFix"
-- to have 4 spaces when TAB is used in the editor
editor.tabwidth = 4
-- to have TABs stored in the file (to allow mixing tabs and spaces)
editor.usetabs = true
-- to disable wrapping of long lines in the editor
editor.usewrap = false
-- to turn dynamic words on and to start suggestions after 4 characters
acandtip.nodynwords = false
acandtip.startat = 4
-- to automatically open files requested during debugging
editor.autoactivate = true
-- to specify a list of MOAI entrypoints
moai = { entrypoints = { "main.lua", "source/main.lua" } }
-- to specify language to use in the IDE (requires a file in cfg/i18n folder)
language = "ru"
-- to change background color (or other colors in the IDE);
-- see cfg/tomorrow.lua for example/details on what other colors to change
styles.text = {bg = {240,240,220}}
-- to change the default color scheme; check tomorrow.lua for the list
-- of supported schemes or use cfg/scheme-picker.lua to pick a scheme.
-- (no longer needed in v1.21+) local G = ... -- this now points to the global environment
styles = loadfile('cfg/tomorrow.lua')('Tomorrow')
-- also apply the same scheme to Output and Console windows
stylesoutshell = styles
-- to change markers used in console and output windows
styles.marker = styles.marker or {}
styles.marker.message = {ch = wxstc.wxSTC_MARK_ARROWS, fg = {0, 0, 0}, bg = {240, 240, 240}}
styles.marker.output = {ch = wxstc.wxSTC_MARK_BACKGROUND, fg = {0, 0, 0}, bg = {240, 240, 240}}
styles.marker.prompt = {ch = wxstc.wxSTC_MARK_CHARACTER+('>'):byte(), fg = {0, 0, 0}, bg = {240, 240, 240}}
stylesoutshell = styles
-- to disable indicators (underlining) on function calls
-- styles.indicator.fncall = nil
-- to change the color of the indicator used for function calls
styles.indicator.fncall.fg = {240,0,0}
-- to change the type of the indicator used for function calls
styles.indicator.fncall.st = wxstc.wxSTC_INDIC_PLAIN
--[[ other possible values are:
wxSTC_INDIC_DOTS Dotted underline; wxSTC_INDIC_PLAIN Single-line underline
wxSTC_INDIC_TT Line of Tshapes; wxSTC_INDIC_SQUIGGLE Squiggly underline
wxSTC_INDIC_STRIKE Strike-out; wxSTC_INDIC_SQUIGGLELOW Squiggly underline (2 pixels)
wxSTC_INDIC_BOX Box; wxSTC_INDIC_ROUNDBOX Rounded Box
wxSTC_INDIC_DASH Dashed underline; wxSTC_INDIC_STRAIGHTBOX Box with trasparency
wxSTC_INDIC_DOTBOX Dotted rectangle; wxSTC_INDIC_DIAGONAL Diagonal hatching
wxSTC_INDIC_HIDDEN No visual effect;
--]]
-- to enable additional spec files (like spec/glsl.lua)
load.specs(function(file) return file:find('spec[/\\]glsl%.lua$') end)
-- to specify a default EOL encoding to be used for new files:
-- `wxstc.wxSTC_EOL_CRLF` or `wxstc.wxSTC_EOL_LF`;
-- `nil` means OS default: CRLF on Windows and LF on Linux/Unix and OSX.
-- (OSX had CRLF as a default until v0.36, which fixed it).
editor.defaulteol = wxstc.wxSTC_EOL_LF
-- to turn off checking for mixed end-of-line encodings in loaded files
editor.checkeol = false
-- to force execution to continue immediately after starting debugging;
-- set to `false` to disable (the interpreter will stop on the first line or
-- when debugging starts); some interpreters may use `true` or `false`
-- by default, but can be still reconfigured with this setting.
debugger.runonstart = true
-- to set compact fold that doesn't include empty lines after a block
editor.foldcompact = true
-- to disable zoom with mouse wheel as it may be too sensitive on OSX
editor.nomousezoom = true
-- to specify a skin for Corona simulator (OSX only);
-- you can also change it between runs from Local Console by executing
-- `ide.config.corona = {skin = 'iPad'}`
corona = { skin = "iPad" }
-- to style individual keywords; `return` and `break` are shown in red
-- (no longer needed in v1.21+) local G = ... -- this now points to the global environment
local luaspec = ide.specs.lua
local num = #luaspec.keywords
-- take a new slot in the list of keywords (starting from 1)
luaspec.keywords[num+1] = 'return break'
-- remove 'return' from the list of "regular" keywords
luaspec.keywords[1] = luaspec.keywords[1]:gsub(' return', ''):gsub(' break', '')
-- assign new style to the added slot (starting from 0)
styles["keywords"..num] = {fg = {240, 0, 0}, b = true}
-- enable `Opt+Shift+Left/Right` shortcut on OSX
editor.keymap[#editor.keymap+1] = {wxstc.wxSTC_KEY_LEFT, wxstc.wxSTC_SCMOD_ALT+wxstc.wxSTC_SCMOD_SHIFT, wxstc.wxSTC_CMD_WORDLEFTEXTEND, "Macintosh"}
editor.keymap[#editor.keymap+1] = {wxstc.wxSTC_KEY_RIGHT, wxstc.wxSTC_SCMOD_ALT+wxstc.wxSTC_SCMOD_SHIFT, wxstc.wxSTC_CMD_WORDRIGHTENDEXTEND, "Macintosh"}
-- enable Emacs bindings to use `Ctrl-A` and `Ctrl-E` to go to the line start/end
editor.keymap[#editor.keymap+1] = {('A'):byte(), wxstc.wxSTC_SCMOD_CTRL, wxstc.wxSTC_CMD_HOME}
editor.keymap[#editor.keymap+1] = {('E'):byte(), wxstc.wxSTC_SCMOD_CTRL, wxstc.wxSTC_CMD_LINEEND}
keymap[ID.SELECTALL] = nil -- remove `Ctrl-A` shortcut from `SelectAll`
-- updated shortcuts to use them as of v1.20
keymap[ID.BREAK] = "Shift-F9"
keymap[ID.BREAKPOINTTOGGLE] = "F9"
keymap[ID.BREAKPOINTNEXT] = ""
keymap[ID.BREAKPOINTPREV] = ""

View File

@ -1,56 +0,0 @@
--[[-- This file shows examples of settings you can adjust.
Configuration files with preferences are loaded in the following order:
1. cfg/user.lua (system-wide configuration)
2. HOME/.zbstudio/user.lua (per-user configuration)
3. -cfg <lua code fragment|filename> (command line configuration)
See [configuration](http://studio.zerobrane.com/doc-configuration.html) page for information about location of configuration files.
--]]--
-- to modify a key mapping; see the full list of IDs in src/editor/keymap.lua
-- starting from v0.95, ID.<menuid> can be used instead of G.ID_<menuid>
keymap[ID.RUN] = "Ctrl-R"
keymap[ID.REPLACE] = nil
-- to change font size to 12
editor.fontsize = 14 -- this is mapped to ide.config.editor.fontsize
--editor.fontname = "Courier New"
filehistorylength = 20 -- this is mapped to ide.config.filehistorylength
-- to specify full path to love2d *executable*; this is only needed
-- if the game folder and the executable are NOT in the same folder.
path.love2d = '../tools/love.app'
-- to disable wrapping of long lines in the editor
editor.usewrap = true
-- to specify language to use in the IDE (requires a file in cfg/i18n folder)
language = "en"
-- do not offer dynamic (user entered) words; set to false to collect all words from all open editor tabs and offer them as part of the auto-complete list.
acandtip.nodynwords = true
-- to change the default color scheme; check tomorrow.lua for the list
-- of supported schemes or use cfg/scheme-picker.lua to pick a scheme.
-- (no longer needed in v1.21+) local G = ... -- this now points to the global environment
styles = loadfile('cfg/tomorrow.lua')('Tomorrow')
--TURN OFF UNDERLINING
styles.indicator = {}
-- also apply the same scheme to Output and Console windows
stylesoutshell = styles
-- to set compact fold that doesn't include empty lines after a block
--editor.foldcompact = true
-- set the default interpreter used for new projects
interpreter = "love2d"
-- https://github.com/pkulchenko/ZeroBraneStudio/issues/645
ide.interpreters.love2d.skipcompile=true
-- don't open output on run
activateoutput = false

View File

@ -1,73 +0,0 @@
-- Copy the content of this file to user.lua;
-- see the [configuration](http://studio.zerobrane.com/doc-configuration.html) page for details.
-- Alt-Shift-Cmd-X (Alt maps to Option, Ctrl maps to Command)
-- The mapping is largely based on [Xcode Keyboard Shortcuts](http://developer.apple.com/library/mac/#documentation/IDEs/Conceptual/xcode_help-command_shortcuts/MenuCommands/MenuCommands014.html).
local xcode = {
-- File menu
[ID.NEW] = "Ctrl-N",
[ID.OPEN] = "Ctrl-O",
[ID.CLOSE] = "Ctrl-W",
[ID.SAVE] = "Ctrl-S",
[ID.SAVEAS] = "Shift-Ctrl-S",
[ID.SAVEALL] = "Alt-Ctrl-S",
[ID.RECENTFILES] = "",
[ID.EXIT] = "Ctrl-Q",
-- Edit menu
[ID.CUT] = "Ctrl-X",
[ID.COPY] = "Ctrl-C",
[ID.PASTE] = "Ctrl-V",
[ID.SELECTALL] = "Ctrl-A",
[ID.UNDO] = "Ctrl-Z",
[ID.REDO] = "Shift-Ctrl-Z",
[ID.SHOWTOOLTIP] = "Ctrl-T",
[ID.AUTOCOMPLETE] = "Ctrl-K",
[ID.AUTOCOMPLETEENABLE] = "",
[ID.COMMENT] = "Ctrl-U",
[ID.FOLD] = "F12",
[ID.CLEARDYNAMICWORDS] = "",
-- Search menu
[ID.FIND] = "Ctrl-F",
[ID.FINDNEXT] = "Ctrl-G",
[ID.FINDPREV] = "Shift-Ctrl-G",
[ID.REPLACE] = "Alt-Ctrl-F",
[ID.FINDINFILES] = "Shift-Ctrl-F",
[ID.REPLACEINFILES] = "Alt-Shift-Ctrl-F",
[ID.SORT] = "",
-- View menu
[ID.VIEWFILETREE] = "Shift-Ctrl-P",
[ID.VIEWOUTPUT] = "Shift-Ctrl-O",
[ID.VIEWWATCHWINDOW] = "Shift-Ctrl-W",
[ID.VIEWCALLSTACK] = "Shift-Ctrl-S",
[ID.VIEWDEFAULTLAYOUT] = "",
[ID.VIEWFULLSCREEN] = "Shift-Ctrl-A",
-- Project menu
[ID.RUN] = "Ctrl-R",
[ID.RUNNOW] = "Shift-Ctrl-R",
[ID.COMPILE] = "Ctrl-B",
[ID.ANALYZE] = "Shift-Ctrl-B",
[ID.STARTDEBUG] = "F5",
[ID.ATTACHDEBUG] = "",
[ID.STOPDEBUG] = "Ctrl-.",
[ID.STEP] = "F7",
[ID.STEPOVER] = "F6",
[ID.STEPOUT] = "F8",
[ID.TRACE] = "",
[ID.BREAK] = "Ctrl-Y",
[ID.BREAKPOINTTOGGLE] = "Ctrl-\\",
[ID.CLEAROUTPUT] = "",
[ID.INTERPRETER] = "",
[ID.PROJECTDIR] = "",
-- Help menu
[ID.ABOUT] = "F1",
-- Watch window menu items
[ID.ADDWATCH] = "Ins",
[ID.EDITWATCH] = "F2",
[ID.DELETEWATCH] = "Del",
-- Editor popup menu items
[ID.QUICKADDWATCH] = "",
[ID.QUICKEVAL] = "",
}
local G = ...
for id, key in G.pairs(xcode) do keymap[id] = key end

View File

@ -1,61 +0,0 @@
-- Copyright 2011-12 Paul Kulchenko, ZeroBrane LLC
local love2d
local win = ide.osname == "Windows"
local mac = ide.osname == "Macintosh"
return {
name = "LÖVE",
description = "LÖVE game engine",
api = {"baselib", "love2d"},
frun = function(self,wfilename,rundebug)
love2d = love2d or ide.config.path.love2d -- check if the path is configured
if not love2d then
local sep = win and ';' or ':'
local default =
win and (GenerateProgramFilesPath('love', sep)..sep)
or mac and ('/Applications/love.app/Contents/MacOS'..sep)
or ''
local path = default
..(os.getenv('PATH') or '')..sep
..(GetPathWithSep(self:fworkdir(wfilename)))..sep
..(os.getenv('HOME') and GetPathWithSep(os.getenv('HOME'))..'bin' or '')
local paths = {}
for p in path:gmatch("[^"..sep.."]+") do
love2d = love2d or GetFullPathIfExists(p, win and 'love.exe' or 'love')
table.insert(paths, p)
end
if not love2d then
DisplayOutputLn("Can't find love2d executable in any of the following folders: "
..table.concat(paths, ", "))
return
end
end
if not GetFullPathIfExists(self:fworkdir(wfilename), 'main.lua') then
DisplayOutputLn(("Can't find 'main.lua' file in the current project folder: '%s'.")
:format(self:fworkdir(wfilename)))
return
end
if rundebug then
DebuggerAttachDefault({runstart = ide.config.debugger.runonstart == true})
end
-- suppress hiding ConsoleWindowClass as this is used by Love console
local uhw = ide.config.unhidewindow
local cwc = uhw and uhw.ConsoleWindowClass
if uhw then uhw.ConsoleWindowClass = 0 end
local params = ide.config.arg.any or ide.config.arg.love2d
local cmd = ('"%s" "%s"%s%s'):format(love2d, self:fworkdir(wfilename),
params and " "..params or "", rundebug and ' -debug' or '')
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
return CommandLineRun(cmd,self:fworkdir(wfilename),true,true,nil,nil,
function() if uhw then uhw.ConsoleWindowClass = cwc end end)
end,
hasdebugger = true,
fattachdebug = function(self) DebuggerAttachDefault() end,
scratchextloop = true,
takeparameters = true,
}

View File

@ -1,101 +0,0 @@
function MakeLuaInterpreter(version, name)
local function exePath(self, version)
local version = tostring(version):gsub('%.','')
local mainpath = ide.editorFilename:gsub("[^/\\]+$","")
local macExe = mainpath..([[bin/lua.app/Contents/MacOS/lua%s]]):format(version)
return ide.config.path['lua'..version]
or (ide.osname == "Windows" and mainpath..([[bin\lua%s.exe]]):format(version))
or (ide.osname == "Unix" and mainpath..([[bin/linux/%s/lua%s]]):format(ide.osarch, version))
or (wx.wxFileExists(macExe) and macExe or mainpath..([[bin/lua%s]]):format(version))
end
return {
name = ("Lua%s"):format(name or version or ""),
description = ("Lua%s interpreter with debugger"):format(name or version or ""),
api = {"baselib"},
luaversion = version or '5.1',
fexepath = exePath,
frun = function(self,wfilename,rundebug)
local exe = self:fexepath(version or "")
local filepath = wfilename:GetFullPath()
do
-- if running on Windows and can't open the file, this may mean that
-- the file path includes unicode characters that need special handling
local fh = io.open(filepath, "r")
if fh then fh:close() end
if ide.osname == 'Windows' and pcall(require, "winapi")
and wfilename:FileExists() and not fh then
winapi.set_encoding(winapi.CP_UTF8)
local shortpath = winapi.short_path(filepath)
if shortpath == filepath then
DisplayOutputLn(
("Can't get short path for a Unicode file name '%s' to open the file.")
:format(filepath))
DisplayOutputLn(
("You can enable short names by using `fsutil 8dot3name set %s: 0` and recreate the file or directory.")
:format(wfilename:GetVolume()))
end
filepath = shortpath
end
end
if rundebug then
DebuggerAttachDefault({runstart = ide.config.debugger.runonstart == true})
-- update arg to point to the proper file
rundebug = ('if arg then arg[0] = [[%s]] end '):format(filepath)..rundebug
local tmpfile = wx.wxFileName()
tmpfile:AssignTempFileName(".")
filepath = tmpfile:GetFullPath()
local f = io.open(filepath, "w")
if not f then
DisplayOutputLn("Can't open temporary file '"..filepath.."' for writing.")
return
end
f:write(rundebug)
f:close()
end
local params = ide.config.arg.any or ide.config.arg.lua
local code = ([[-e "io.stdout:setvbuf('no')" "%s"]]):format(filepath)
local cmd = '"'..exe..'" '..code..(params and " "..params or "")
-- modify CPATH to work with other Lua versions
local envname = "LUA_CPATH"
if version then
local env = "LUA_CPATH_"..string.gsub(version, '%.', '_')
if os.getenv(env) then envname = env end
end
local cpath = os.getenv(envname)
if rundebug and cpath and not ide.config.path['lua'..(version or "")] then
-- prepend osclibs as the libraries may be needed for debugging,
-- but only if no path.lua is set as it may conflict with system libs
wx.wxSetEnv(envname, ide.osclibs..';'..cpath)
end
if version and cpath then
local cpath = os.getenv(envname)
local clibs = string.format('/clibs%s/', version):gsub('%.','')
if not cpath:find(clibs, 1, true) then cpath = cpath:gsub('/clibs/', clibs) end
wx.wxSetEnv(envname, cpath)
end
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
local pid = CommandLineRun(cmd,self:fworkdir(wfilename),true,false,nil,nil,
function() if rundebug then wx.wxRemoveFile(filepath) end end)
if (rundebug or version) and cpath then wx.wxSetEnv(envname, cpath) end
return pid
end,
hasdebugger = true,
fattachdebug = function(self) DebuggerAttachDefault() end,
scratchextloop = false,
unhideanywindow = true,
takeparameters = true,
}
end
return nil -- as this is not a real interpreter

View File

@ -1,2 +0,0 @@
dofile 'interpreters/luabase.lua'
return MakeLuaInterpreter()

View File

@ -1,2 +0,0 @@
dofile 'interpreters/luabase.lua'
return MakeLuaInterpreter(5.2, ' 5.2')

View File

@ -1,4 +0,0 @@
dofile 'interpreters/luabase.lua'
local interpreter = MakeLuaInterpreter(5.3, ' 5.3')
interpreter.skipcompile = true
return interpreter

View File

@ -1,636 +0,0 @@
-------------------------------------------------------------------------------
-- Copas - Coroutine Oriented Portable Asynchronous Services
--
-- A dispatcher based on coroutines that can be used by TCP/IP servers.
-- Uses LuaSocket as the interface with the TCP/IP stack.
--
-- Authors: Andre Carregal, Javier Guerra, and Fabio Mascarenhas
-- Contributors: Diego Nehab, Mike Pall, David Burgess, Leonardo Godinho,
-- Thomas Harning Jr., and Gary NG
--
-- Copyright 2005 - Kepler Project (www.keplerproject.org)
--
-- $Id: copas.lua,v 1.37 2009/04/07 22:09:52 carregal Exp $
-------------------------------------------------------------------------------
if package.loaded["socket.http"] then
error("you must require copas before require'ing socket.http")
end
local socket = require "socket"
local gettime = socket.gettime
local coxpcall = require "coxpcall"
local WATCH_DOG_TIMEOUT = 120
local UDP_DATAGRAM_MAX = 8192
-- Redefines LuaSocket functions with coroutine safe versions
-- (this allows the use of socket.http from within copas)
local function statusHandler(status, ...)
if status then return ... end
local err = (...)
if type(err) == "table" then
return nil, err[1]
else
error(err)
end
end
function socket.protect(func)
return function (...)
return statusHandler(coxpcall.pcall(func, ...))
end
end
function socket.newtry(finalizer)
return function (...)
local status = (...)
if not status then
coxpcall.pcall(finalizer, select(2, ...))
error({ (select(2, ...)) }, 0)
end
return ...
end
end
-- end of LuaSocket redefinitions
local copas = {}
-- Meta information is public even if beginning with an "_"
copas._COPYRIGHT = "Copyright (C) 2005-2010 Kepler Project"
copas._DESCRIPTION = "Coroutine Oriented Portable Asynchronous Services"
copas._VERSION = "Copas 1.2.1"
-- Close the socket associated with the current connection after the handler finishes
copas.autoclose = true
-------------------------------------------------------------------------------
-- Simple set implementation based on LuaSocket's tinyirc.lua example
-- adds a FIFO queue for each value in the set
-------------------------------------------------------------------------------
local function newset()
local reverse = {}
local set = {}
local q = {}
setmetatable(set, { __index = {
insert = function(set, value)
if not reverse[value] then
set[#set + 1] = value
reverse[value] = #set
end
end,
remove = function(set, value)
local index = reverse[value]
if index then
reverse[value] = nil
local top = set[#set]
set[#set] = nil
if top ~= value then
reverse[top] = index
set[index] = top
end
end
end,
push = function (set, key, itm)
local qKey = q[key]
if qKey == nil then
q[key] = {itm}
else
qKey[#qKey + 1] = itm
end
end,
pop = function (set, key)
local t = q[key]
if t ~= nil then
local ret = table.remove (t, 1)
if t[1] == nil then
q[key] = nil
end
return ret
end
end
}})
return set
end
local fnil = function()end
local _sleeping = {
times = {}, -- list with wake-up times
cos = {}, -- list with coroutines, index matches the 'times' list
lethargy = {}, -- list of coroutines sleeping without a wakeup time
insert = fnil,
remove = fnil,
push = function(self, sleeptime, co)
if not co then return end
if sleeptime<0 then
--sleep until explicit wakeup through copas.wakeup
self.lethargy[co] = true
return
else
sleeptime = gettime() + sleeptime
end
local t, c = self.times, self.cos
local i, cou = 1, #t
--TODO: do a binary search
while i<=cou and t[i]<=sleeptime do i=i+1 end
table.insert(t, i, sleeptime)
table.insert(c, i, co)
end,
getnext = function(self) -- returns delay until next sleep expires, or nil if there is none
local t = self.times
local delay = t[1] and t[1] - gettime() or nil
return delay and math.max(delay, 0) or nil
end,
-- find the thread that should wake up to the time
pop = function(self, time)
local t, c = self.times, self.cos
if #t==0 or time<t[1] then return end
local co = c[1]
table.remove(t, 1)
table.remove(c, 1)
return co
end,
wakeup = function(self, co)
local let = self.lethargy
if let[co] then
self:push(0, co)
let[co] = nil
else
let = self.cos
for i=1,#let do
if let[i]==co then
table.remove(let, i)
local tm = self.times[i]
table.remove(self.times, i)
self:push(0, co)
return
end
end
end
end
} --_sleeping
local _servers = newset() -- servers being handled
local _reading_log = {}
local _writing_log = {}
local _reading = newset() -- sockets currently being read
local _writing = newset() -- sockets currently being written
-------------------------------------------------------------------------------
-- Coroutine based socket I/O functions.
-------------------------------------------------------------------------------
-- reads a pattern from a client and yields to the reading set on timeouts
-- UDP: a UDP socket expects a second argument to be a number, so it MUST
-- be provided as the 'pattern' below defaults to a string. Will throw a
-- 'bad argument' error if omitted.
function copas.receive(client, pattern, part)
local s, err
pattern = pattern or "*l"
repeat
s, err, part = client:receive(pattern, part)
if s or err ~= "timeout" then
_reading_log[client] = nil
return s, err, part
end
_reading_log[client] = gettime()
coroutine.yield(client, _reading)
until false
end
-- receives data from a client over UDP. Not available for TCP.
-- (this is a copy of receive() method, adapted for receivefrom() use)
function copas.receivefrom(client, size)
local s, err, port
size = size or UDP_DATAGRAM_MAX
repeat
s, err, port = client:receivefrom(size) -- upon success err holds ip address
if s or err ~= "timeout" then
_reading_log[client] = nil
return s, err, port
end
_reading_log[client] = gettime()
coroutine.yield(client, _reading)
until false
end
-- same as above but with special treatment when reading chunks,
-- unblocks on any data received.
function copas.receivePartial(client, pattern, part)
local s, err
pattern = pattern or "*l"
repeat
s, err, part = client:receive(pattern, part)
if s or ( (type(pattern)=="number") and part~="" and part ~=nil ) or
err ~= "timeout" then
_reading_log[client] = nil
return s, err, part
end
_reading_log[client] = gettime()
coroutine.yield(client, _reading)
until false
end
-- sends data to a client. The operation is buffered and
-- yields to the writing set on timeouts
-- Note: from and to parameters will be ignored by/for UDP sockets
function copas.send(client, data, from, to)
local s, err,sent
from = from or 1
local lastIndex = from - 1
repeat
s, err, lastIndex = client:send(data, lastIndex + 1, to)
-- adds extra corrotine swap
-- garantees that high throuput dont take other threads to starvation
if (math.random(100) > 90) then
_writing_log[client] = gettime()
coroutine.yield(client, _writing)
end
if s or err ~= "timeout" then
_writing_log[client] = nil
return s, err,lastIndex
end
_writing_log[client] = gettime()
coroutine.yield(client, _writing)
until false
end
-- sends data to a client over UDP. Not available for TCP.
-- (this is a copy of send() method, adapted for sendto() use)
function copas.sendto(client, data, ip, port)
local s, err,sent
repeat
s, err = client:sendto(data, ip, port)
-- adds extra corrotine swap
-- garantees that high throuput dont take other threads to starvation
if (math.random(100) > 90) then
_writing_log[client] = gettime()
coroutine.yield(client, _writing)
end
if s or err ~= "timeout" then
_writing_log[client] = nil
return s, err
end
_writing_log[client] = gettime()
coroutine.yield(client, _writing)
until false
end
-- waits until connection is completed
function copas.connect(skt, host, port)
skt:settimeout(0)
local ret, err
repeat
ret, err = skt:connect (host, port)
if ret or err ~= "timeout" then
_writing_log[skt] = nil
return ret, err
end
_writing_log[skt] = gettime()
coroutine.yield(skt, _writing)
until false
return ret, err
end
-- flushes a client write buffer (deprecated)
function copas.flush(client)
end
-- wraps a TCP socket to use Copas methods (send, receive, flush and settimeout)
local _skt_mt = {__index = {
send = function (self, data, from, to)
return copas.send (self.socket, data, from, to)
end,
receive = function (self, pattern, prefix)
if (self.timeout==0) then
return copas.receivePartial(self.socket, pattern, prefix)
end
return copas.receive(self.socket, pattern, prefix)
end,
flush = function (self)
return copas.flush(self.socket)
end,
settimeout = function (self,time)
self.timeout=time
return true
end,
skip = function(self, ...) return self.socket:skip(...) end,
close = function(self, ...) return self.socket:close(...) end,
}}
-- wraps a UDP socket, copy of TCP one adapted for UDP.
-- Mainly adds sendto() and receivefrom()
local _skt_mt_udp = {__index = {
send = function (self, data)
return copas.send (self.socket, data)
end,
sendto = function (self, data, ip, port)
return copas.sendto (self.socket, data, ip, port)
end,
receive = function (self, size)
return copas.receive (self.socket, (size or UDP_DATAGRAM_MAX))
end,
receivefrom = function (self, size)
return copas.receivefrom (self.socket, (size or UDP_DATAGRAM_MAX))
end,
flush = function (self)
return copas.flush (self.socket)
end,
settimeout = function (self,time)
self.timeout=time
return true
end,
}}
function copas.wrap (skt)
if string.sub(tostring(skt),1,3) == "udp" then
return setmetatable ({socket = skt}, _skt_mt_udp)
else
return setmetatable ({socket = skt}, _skt_mt)
end
end
--------------------------------------------------
-- Error handling
--------------------------------------------------
local _errhandlers = {} -- error handler per coroutine
function copas.setErrorHandler (err)
local co = coroutine.running()
if co then
_errhandlers [co] = err
end
end
local function _deferror (msg, co, skt)
print (msg, co, skt)
end
-------------------------------------------------------------------------------
-- Thread handling
-------------------------------------------------------------------------------
local function _doTick (co, skt, ...)
if not co then return end
local ok, res, new_q = coroutine.resume(co, skt, ...)
if ok and res and new_q then
new_q:insert (res)
new_q:push (res, co)
else
if not ok then coxpcall.pcall (_errhandlers [co] or _deferror, res, co, skt) end
if skt and copas.autoclose then skt:close() end
_errhandlers [co] = nil
end
end
-- accepts a connection on socket input
local function _accept(input, handler)
local client = input:accept()
if client then
client:settimeout(0)
local co = coroutine.create(handler)
_doTick (co, client)
--_reading:insert(client)
end
return client
end
-- handle threads on a queue
local function _tickRead (skt)
_doTick (_reading:pop (skt), skt)
end
local function _tickWrite (skt)
_doTick (_writing:pop (skt), skt)
end
-------------------------------------------------------------------------------
-- Adds a server/handler pair to Copas dispatcher
-------------------------------------------------------------------------------
local function addTCPserver(server, handler, timeout)
server:settimeout(timeout or 0.1)
_servers[server] = handler
_reading:insert(server)
end
local function addUDPserver(server, handler, timeout)
server:settimeout(timeout or 0)
local co = coroutine.create(handler)
_reading:insert(server)
_doTick (co, server)
end
function copas.addserver(server, handler, timeout)
if string.sub(tostring(server),1,3) == "udp" then
addUDPserver(server, handler, timeout)
else
addTCPserver(server, handler, timeout)
end
end
function copas.removeserver(server)
_servers[server] = nil
_reading:remove(server)
return server:close()
end
-------------------------------------------------------------------------------
-- Adds an new courotine thread to Copas dispatcher
-------------------------------------------------------------------------------
function copas.addthread(thread, ...)
if type(thread) ~= "thread" then
thread = coroutine.create(thread)
end
_doTick (thread, nil, ...)
return thread
end
-------------------------------------------------------------------------------
-- tasks registering
-------------------------------------------------------------------------------
local _tasks = {}
local function addtaskRead (tsk)
-- lets tasks call the default _tick()
tsk.def_tick = _tickRead
_tasks [tsk] = true
end
local function addtaskWrite (tsk)
-- lets tasks call the default _tick()
tsk.def_tick = _tickWrite
_tasks [tsk] = true
end
local function tasks ()
return next, _tasks
end
-------------------------------------------------------------------------------
-- main tasks: manage readable and writable socket sets
-------------------------------------------------------------------------------
-- a task to check ready to read events
local _readable_t = {
events = function(self)
local i = 0
return function ()
i = i + 1
return self._evs [i]
end
end,
tick = function (self, input)
local handler = _servers[input]
if handler then
input = _accept(input, handler)
else
_reading:remove (input)
self.def_tick (input)
end
end
}
addtaskRead (_readable_t)
-- a task to check ready to write events
local _writable_t = {
events = function (self)
local i = 0
return function ()
i = i + 1
return self._evs [i]
end
end,
tick = function (self, output)
_writing:remove (output)
self.def_tick (output)
end
}
addtaskWrite (_writable_t)
--
--sleeping threads task
local _sleeping_t = {
tick = function (self, time, ...)
_doTick(_sleeping:pop(time), ...)
end
}
-- yields the current coroutine and wakes it after 'sleeptime' seconds.
-- If sleeptime<0 then it sleeps until explicitly woken up using 'wakeup'
function copas.sleep(sleeptime)
coroutine.yield((sleeptime or 0), _sleeping)
end
-- Wakes up a sleeping coroutine 'co'.
function copas.wakeup(co)
_sleeping:wakeup(co)
end
local last_cleansing = 0
-------------------------------------------------------------------------------
-- Checks for reads and writes on sockets
-------------------------------------------------------------------------------
local function _select (timeout)
local err
local now = gettime()
local duration = function(t2, t1) return t2-t1 end
_readable_t._evs, _writable_t._evs, err = socket.select(_reading, _writing, timeout)
local r_evs, w_evs = _readable_t._evs, _writable_t._evs
if duration(now, last_cleansing) > WATCH_DOG_TIMEOUT then
last_cleansing = now
for k,v in pairs(_reading_log) do
if not r_evs[k] and duration(now, v) > WATCH_DOG_TIMEOUT then
_reading_log[k] = nil
r_evs[#r_evs + 1] = k
r_evs[k] = #r_evs
end
end
for k,v in pairs(_writing_log) do
if not w_evs[k] and duration(now, v) > WATCH_DOG_TIMEOUT then
_writing_log[k] = nil
w_evs[#w_evs + 1] = k
w_evs[k] = #w_evs
end
end
end
if err == "timeout" and #r_evs + #w_evs > 0 then
return nil
else
return err
end
end
-------------------------------------------------------------------------------
-- Dispatcher loop step.
-- Listen to client requests and handles them
-- Returns false if no data was handled (timeout), or true if there was data
-- handled (or nil + error message)
-------------------------------------------------------------------------------
function copas.step(timeout)
_sleeping_t:tick(gettime())
-- Need to wake up the select call it time for the next sleeping event
local nextwait = _sleeping:getnext()
if nextwait then
timeout = timeout and math.min(nextwait, timeout) or nextwait
end
local err = _select (timeout)
if err == "timeout" then return false end
if err then
error(err)
end
for tsk in tasks() do
for ev in tsk:events() do
tsk:tick (ev)
end
end
return true
end
-------------------------------------------------------------------------------
-- Dispatcher endless loop.
-- Listen to client requests and handles them forever
-------------------------------------------------------------------------------
function copas.loop(timeout)
while true do
copas.step(timeout)
end
end
return copas

View File

@ -1,68 +0,0 @@
-------------------------------------------------------------------------------
-- Coroutine safe xpcall and pcall versions
--
-- Encapsulates the protected calls with a coroutine based loop, so errors can
-- be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines
-- yielding inside the call to pcall or xpcall.
--
-- Authors: Roberto Ierusalimschy and Andre Carregal
-- Contributors: Thomas Harning Jr., Ignacio Burgueño, Fabio Mascarenhas
--
-- Copyright 2005 - Kepler Project (www.keplerproject.org)
--
-- $Id: coxpcall.lua,v 1.13 2008/05/19 19:20:02 mascarenhas Exp $
-------------------------------------------------------------------------------
-- Lua 5.2 makes this module a no-op
if _VERSION == "Lua 5.2" then
copcall = pcall
coxpcall = xpcall
return { pcall = pcall, xpcall = xpcall }
end
-------------------------------------------------------------------------------
-- Implements xpcall with coroutines
-------------------------------------------------------------------------------
local performResume, handleReturnValue
local oldpcall, oldxpcall = pcall, xpcall
local pack = table.pack or function(...) return {n = select("#", ...), ...} end
local unpack = table.unpack or unpack
function handleReturnValue(err, co, status, ...)
if not status then
return false, err(debug.traceback(co, (...)), ...)
end
if coroutine.status(co) == 'suspended' then
return performResume(err, co, coroutine.yield(...))
else
return true, ...
end
end
function performResume(err, co, ...)
return handleReturnValue(err, co, coroutine.resume(co, ...))
end
function coxpcall(f, err, ...)
local res, co = oldpcall(coroutine.create, f)
if not res then
local params = pack(...)
local newf = function() return f(unpack(params, 1, params.n)) end
co = coroutine.create(newf)
end
return performResume(err, co, ...)
end
-------------------------------------------------------------------------------
-- Implements pcall with coroutines
-------------------------------------------------------------------------------
local function id(trace, ...)
return ...
end
function copcall(f, ...)
return coxpcall(f, id, ...)
end
return { pcall = copcall, xpcall = coxpcall }

View File

@ -1,112 +0,0 @@
-- Luadist configuration
module ("dist.config", package.seeall)
local sys = require "dist.sys"
local utils = require "dist.utils"
local win = (os.getenv('WINDIR') or (os.getenv('OS') or ''):match('[Ww]indows'))
and not (os.getenv('OSTYPE') or ''):match('cygwin') -- exclude cygwin
-- System information ------------------------------------------------
version = "0.2.7" -- Current LuaDist version
-- set initial architecture as it's important for path separators
arch = win and "Windows" or "Linux" -- Host architecture
type = "x86" -- Host type
-- Directories -------------------------------------------------------
root_dir = os.getenv("DIST_ROOT") or utils.get_luadist_location() or sys.path_separator()
temp_dir = "tmp"
cache_dir = sys.make_path(temp_dir, "cache")
distinfos_dir = sys.make_path("share", "luadist-git", "dists")
test_dir = sys.make_path("share", "luadist-git", "test")
-- Files -------------------------------------------------------------
manifest_file = sys.make_path(cache_dir, ".gitmodules")
dep_cache_file = sys.make_path(cache_dir, ".depcache")
log_file = sys.make_path(temp_dir, "luadist.log")
cache_file = ""
-- Repositories ------------------------------------------------------
repos = {
"git://github.com/LuaDist/Repository.git",
}
upload_url = "git@github.com:LuaDist" -- must not contain trailing '/'
-- Settings ----------------------------------------------------------
debug = false -- Use debug mode.
verbose = false -- Print verbose output.
simulate = false -- Only simulate installation of packages.
binary = true -- Use binary version of modules.
source = true -- Use source version of modules.
test = false -- Run CTest before install.
cache = true -- Use cache.
cache_timeout = 3 * 60 * 60 -- Cache timeout in seconds.
dep_cache = true -- Use cache for dependency information (tree functionality).
-- Components (of modules) that will be installed.
components = {
"Runtime", "Library", "Header", "Data", "Documentation", "Example", "Test", "Other", "Unspecified"
}
-- Available log levels are: DEBUG, INFO, WARN, ERROR, FATAL (see dist.logger for more information).
print_log_level = "WARN" -- Minimum level for log messages to be printed (nil to disable).
write_log_level = "INFO" -- Minimum level for log messages to be logged (nil to disable).
-- CMake variables ---------------------------------------------------
variables = {
--- Install defaults
INSTALL_BIN = "bin",
INSTALL_LIB = "lib",
INSTALL_INC = "include",
INSTALL_ETC = "etc",
INSTALL_LMOD = "lib/lua",
INSTALL_CMOD = "lib/lua",
--- LuaDist specific variables
DIST_VERSION = version,
DIST_ARCH = arch,
DIST_TYPE = type,
-- CMake specific setup
CMAKE_GENERATOR = win and "MinGW Makefiles" or "Unix Makefiles",
CMAKE_BUILD_TYPE = "MinSizeRel",
-- RPath functionality
CMAKE_SKIP_BUILD_RPATH = "FALSE",
CMAKE_BUILD_WITH_INSTALL_RPATH = "FALSE",
CMAKE_INSTALL_RPATH = "$ORIGIN/../lib",
CMAKE_INSTALL_RPATH_USE_LINK_PATH = "TRUE",
CMAKE_INSTALL_NAME_DIR = "@executable_path/../lib",
-- OSX specific
CMAKE_OSX_ARCHITECTURES = "",
}
-- Building ----------------------------------------------------------
cmake = "cmake"
ctest = "ctest"
cache_command = cmake .. " -C cache.cmake"
build_command = cmake .. " --build . --clean-first"
install_component_command = " -DCOMPONENT=#COMPONENT# -P cmake_install.cmake"
test_command = ctest .. " -V ."
strip_option = " -DCMAKE_INSTALL_DO_STRIP=true"
cache_debug_options = "-DCMAKE_VERBOSE_MAKEFILE=true -DCMAKE_BUILD_TYPE=Debug"
build_debug_options = ""
-- Add -j option to make in case of unix makefiles to speed up builds
if (variables.CMAKE_GENERATOR == "Unix Makefiles") then
build_command = build_command .. " -- -j6"
end
-- Add -j option to make in case of MinGW makefiles to speed up builds
if (variables.CMAKE_GENERATOR == "MinGW Makefiles") then
build_command = "set SHELL=cmd.exe && " .. build_command .. " -- -j"
end

View File

@ -1,271 +0,0 @@
-- Note: the code of this module is borrowed from the original LuaDist project
--- LuaDist version constraints functions
-- Peter Drahoš, LuaDist Project, 2010
-- Original Code borrowed from LuaRocks Project
--- Version constraints handling functions.
-- Dependencies are represented in LuaDist through strings with
-- a dist 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, LuaDist 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 LuaDist provides some
-- insights on what these criteria are.
module ("dist.constraints", package.seeall)
local operators = {
["=="] = "==",
["~="] = "~=",
[">"] = ">",
["<"] = "<",
[">="] = ">=",
["<="] = "<=",
["~>"] = "~>",
-- plus some convenience translations
[""] = "==",
["-"] = "==",
["="] = "==",
["!="] = "~="
}
local deltas = {
scm = -100,
rc = -1000,
pre = -10000,
beta = -100000,
alpha = -1000000,
work = -10000000,
}
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 parseVersion(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
return nil
end
local last = #version
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 compareVersions(a, b)
return parseVersion(a) > parseVersion(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 parseConstraint(input)
assert(type(input) == "string")
local op, version, rest = input:match("^([<>=~!]*)%s*([%w%.%_%-]+)[%s,]*(.*)")
op = operators[op]
version = parseVersion(version)
if not op or not version then return nil end
return { op = op, version = version }, 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 parseConstraints(input)
assert(type(input) == "string")
local constraints, constraint = {}, nil
while #input > 0 do
constraint, input = parseConstraint(input)
if constraint then
table.insert(constraints, constraint)
else
return nil
end
end
return constraints
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 partialMatch(version, requested)
assert(type(version) == "string" or type(version) == "table")
assert(type(requested) == "string" or type(version) == "table")
if type(version) ~= "table" then version = parseVersion(version) end
if type(requested) ~= "table" then requested = parseVersion(requested) end
if not version or not requested then return false end
for i = 1, #requested do
if requested[i] ~= version[i] 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 matchConstraints(version, constraints)
assert(type(version) == "table")
assert(type(constraints) == "table")
local ok = true
setmetatable(version, version_mt)
for _, constr in pairs(constraints) do
local constr_version = constr.version
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 = partialMatch(version, constr_version)
end
if not ok then break end
end
return ok
end
--- Check if a version string is satisfied by a constraint string.
-- @param version string: A version in string format
-- @param constraints string: Constraints in string format.
-- @return boolean: True if version satisfies all constraints,
-- false otherwise.
function constraint_satisfied(version, constraints)
local const = parseConstraints(constraints)
local ver = parseVersion(version)
if const and ver then
return matchConstraints(ver, const)
end
return nil, "Error parsing versions."
end

View File

@ -1,770 +0,0 @@
-- Utility functions for dependencies
module ("dist.depends", package.seeall)
local cfg = require "dist.config"
local mf = require "dist.manifest"
local sys = require "dist.sys"
local const = require "dist.constraints"
local utils = require "dist.utils"
local package = require "dist.package"
-- Return all packages with specified names from manifest.
-- Names can also contain version constraint (e.g. 'copas>=1.2.3', 'saci-1.0' etc.).
function find_packages(package_names, manifest)
if type(package_names) == "string" then package_names = {package_names} end
manifest = manifest or mf.get_manifest()
assert(type(package_names) == "table", "depends.find_packages: Argument 'package_names' is not a table or string.")
assert(type(manifest) == "table", "depends.find_packages: Argument 'manifest' is not a table.")
local packages_found = {}
-- find matching packages in manifest
for _, pkg_to_find in pairs(package_names) do
local pkg_name, pkg_constraint = split_name_constraint(pkg_to_find)
pkg_name = utils.escape_magic(pkg_name):gsub("%%%*",".*")
for _, repo_pkg in pairs(manifest) do
if string.match(repo_pkg.name, "^" .. pkg_name .. "$") and (not pkg_constraint or satisfies_constraint(repo_pkg.version, pkg_constraint)) then
table.insert(packages_found, repo_pkg)
end
end
end
return packages_found
end
-- Return manifest consisting of packages installed in specified deploy_dir directory
function get_installed(deploy_dir)
deploy_dir = deploy_dir or cfg.root_dir
assert(type(deploy_dir) == "string", "depends.get_installed: Argument 'deploy_dir' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
local distinfos_path = sys.make_path(deploy_dir, cfg.distinfos_dir)
local manifest = {}
if not sys.is_dir(distinfos_path) then return {} end
-- from all directories of packages installed in deploy_dir
for dir in sys.get_directory(distinfos_path) do
if dir ~= "." and dir ~= ".." and sys.is_dir(sys.make_path(distinfos_path, dir)) then
local pkg_dist_dir = sys.make_path(distinfos_path, dir)
-- load the dist.info file
for file in sys.get_directory(pkg_dist_dir) do
local pkg_dist_file = sys.make_path(pkg_dist_dir, file)
if sys.is_file(pkg_dist_file) then
table.insert(manifest, mf.load_distinfo(pkg_dist_file))
end
end
end
end
return manifest
end
-- If 'pkg.selected' == true then returns 'selected' else 'installed'.
-- Used in error messages.
local function selected_or_installed(pkg)
assert(type(pkg) == "table", "depends.selected_or_installed: Argument 'pkg' is not a table.")
if pkg.selected == true then
return "selected"
else
return "installed"
end
end
-- Return whether the 'package_name' is installed according to the the manifest 'installed_pkgs'
-- If optional 'version_wanted' constraint is specified, then installed packages must
-- also satisfy specified version constraint.
-- If package is installed but doesn't satisfy version constraint, error message
-- is returned as the second value.
function is_installed(package_name, installed_pkgs, version_wanted)
assert(type(package_name) == "string", "depends.is_installed: Argument 'package_name' is not a string.")
assert(type(installed_pkgs) == "table", "depends.is_installed: Argument 'installed_pkgs' is not a table.")
assert(type(version_wanted) == "string" or type(version_wanted) == "nil", "depends.is_installed: Argument 'version_wanted' is not a string or nil.")
local pkg_is_installed, err = false, nil
for _, installed_pkg in pairs(installed_pkgs) do
-- check if package_name is in installed
if package_name == installed_pkg.name then
-- check if package is installed in satisfying version
if not version_wanted or satisfies_constraint(installed_pkg.version, version_wanted) then
pkg_is_installed = true
break
else
err = "Package '" .. package_name .. (version_wanted and " " .. version_wanted or "") .. "' needed, but " .. selected_or_installed(installed_pkg) .. " at version '" .. installed_pkg.version .. "'."
break
end
end
end
return pkg_is_installed, err
end
-- Check whether the package 'pkg' conflicts with 'installed_pkg' and return
-- false or error message.
local function packages_conflicts(pkg, installed_pkg)
assert(type(pkg) == "table", "depends.packages_conflicts: Argument 'pkg' is not a table.")
assert(type(installed_pkg) == "table", "depends.packages_conflicts: Argument 'installed_pkg' is not a table.")
-- check if pkg doesn't provide an already installed_pkg
if pkg.provides then
-- for all of pkg's provides
for _, provided_pkg in pairs(get_provides(pkg)) do
if provided_pkg.name == installed_pkg.name then
return "Package '" .. pkg_full_name(pkg.name, pkg.version, pkg.was_scm_version) .. "' provides '" .. pkg_full_name(provided_pkg.name, provided_pkg.version) .. "' but package '" .. pkg_full_name(installed_pkg.name, installed_pkg.version) .. "' is already " .. selected_or_installed(installed_pkg) .. "."
end
end
end
-- check for conflicts of package to install with installed package
if pkg.conflicts then
for _, conflict in pairs (pkg.conflicts) do
if conflict == installed_pkg.name then
return "Package '" .. pkg_full_name(pkg.name, pkg.version, pkg.was_scm_version) .. "' conflicts with already " .. selected_or_installed(installed_pkg) .. " package '" .. pkg_full_name(installed_pkg.name, installed_pkg.version) .. "'."
end
end
end
-- check for conflicts of installed package with package to install
if installed_pkg.conflicts then
-- direct conflicts with 'pkg'
for _, conflict in pairs (installed_pkg.conflicts) do
if conflict == pkg.name then
return "Already " .. selected_or_installed(installed_pkg) .. " package '" .. pkg_full_name(installed_pkg.name, installed_pkg.version) .. "' conflicts with package '" .. pkg_full_name(pkg.name, pkg.version, pkg.was_scm_version) .. "'."
end
end
-- conflicts with 'provides' of 'pkg' (packages provided by package to install)
if pkg.provides then
for _, conflict in pairs (installed_pkg.conflicts) do
-- for all of pkg's provides
for _, provided_pkg in pairs(get_provides(pkg)) do
if conflict == provided_pkg.name then
return "Already '" .. selected_or_installed(installed_pkg) .. " package '" .. pkg_full_name(installed_pkg.name, installed_pkg.version) .. "' conflicts with package '" .. pkg_full_name(provided_pkg.name, provided_pkg.version) .. "' provided by '" .. pkg_full_name(pkg.name, pkg.version, pkg.was_scm_version) .. "'."
end
end
end
end
end
-- no conflicts found
return false
end
-- Return table of package dependencies 'depends' with OS specific dependencies extracted.
--
-- OS specific dependencies are stored in a subtable with 'arch' as a key.
-- E.g. this table containing OS specific dependencies:
-- depends = {
-- "lua~>5.1",
-- "luadist-git>=0.1",
-- Linux = {
-- "iup>=3.6",
-- "wxlua>=2.8.10.0",
-- },
-- Windows = {
-- "luagd>=2.0.33r2",
-- "luacom>=1.4.1",
-- },
-- }
--
-- ...will be on the 'Linux' architecture (determined by cfg.arch) converted into:
-- depends = {
-- "lua~>5.1",
-- "luadist-git>=0.1",
-- "iup>=3.6",
-- "wxlua>=2.8.10.0",
-- }
function extract_os_specific_depends(depends)
assert(type(depends) == "table", "depends.extract_os_specific_depends: Argument 'depends' is not a table.")
local extracted = {}
for k, depend in pairs(depends) do
-- if 'depend' is a table, then it must be a table of OS specific
-- dependencies, so extract it if it's for this architecture
if type(depend) == "table" then
if k == cfg.arch then
for _, os_specific_depend in pairs(depend) do
table.insert(extracted, os_specific_depend)
end
end
else
table.insert(extracted, depend)
end
end
return extracted
end
-- Return all packages needed in order to install package 'pkg'
-- and with specified 'installed' packages in the system using 'manifest'.
-- 'pkg' can also contain version constraint (e.g. 'copas>=1.2.3', 'saci-1.0' etc.).
--
-- This function also downloads packages to get information about their dependencies.
-- Directory where the package was downloaded is stored in 'download_dir' attribute
-- of that package in the table of packages returned by this function.
--
-- Optional argument 'dependency_manifest' is a table of dependencies examined
-- from previous installations etc. It can be used to speed-up the dependency
-- resolving procedure for example.
--
-- When optional 'force_no_download' parameter is set to true, then information
-- about packages won't be downloaded during dependency resolving, assuming that
-- entries in the provided manifest are already complete.
--
-- When optional 'suppress_printing' parameter is set to true, then messages
-- for the user won't be printed during dependency resolving.
--
-- Optional argument 'deploy_dir' is used just as a temporary place to place
-- the downloaded packages into.
--
-- 'dependency_parents' is table of all packages encountered so far when resolving dependencies
-- and is used to detect and deal with circular dependencies. Leave it 'nil'
-- and it will do its job just fine :-).
--
-- 'tmp_installed' is internal table used in recursion and should be left 'nil' when
-- calling this function from other context. It is used for passing the changes
-- in installed packages between the recursive calls of this function.
--
-- TODO: refactor this spaghetti code!
local function get_packages_to_install(pkg, installed, manifest, dependency_manifest, force_no_download, suppress_printing, deploy_dir, dependency_parents, tmp_installed)
manifest = manifest or mf.get_manifest()
dependency_manifest = dependency_manifest or {}
force_no_download = force_no_download or false
suppress_printing = suppress_printing or false
deploy_dir = deploy_dir or cfg.root_dir
dependency_parents = dependency_parents or {}
-- set helper table 'tmp_installed'
tmp_installed = tmp_installed or utils.deepcopy(installed)
assert(type(pkg) == "string", "depends.get_packages_to_install: Argument 'pkg' is not a string.")
assert(type(installed) == "table", "depends.get_packages_to_install: Argument 'installed' is not a table.")
assert(type(manifest) == "table", "depends.get_packages_to_install: Argument 'manifest' is not a table.")
assert(type(dependency_manifest) == "table", "depends.get_packages_to_install: Argument 'dependency_manifest' is not a table.")
assert(type(force_no_download) == "boolean", "depends.get_packages_to_install: Argument 'force_no_download' is not a boolean.")
assert(type(suppress_printing) == "boolean", "depends.get_packages_to_install: Argument 'suppress_printing' is not a boolean.")
assert(type(deploy_dir) == "string", "depends.get_packages_to_install: Argument 'deploy_dir' is not a string.")
assert(type(dependency_parents) == "table", "depends.get_packages_to_install: Argument 'dependency_parents' is not a table.")
assert(type(tmp_installed) == "table", "depends.get_packages_to_install: Argument 'tmp_installed' is not a table.")
deploy_dir = sys.abs_path(deploy_dir)
--[[ for future debugging:
print('resolving: '.. pkg)
print(' installed: ', utils.table_tostring(installed))
print(' tmp_installed: ', utils.table_tostring(tmp_installed))
--]]
-- check if package is already installed
local pkg_name, pkg_constraint = split_name_constraint(pkg)
local pkg_is_installed, err = is_installed(pkg_name, tmp_installed, pkg_constraint)
if pkg_is_installed then return {} end
if err then return nil, err end
-- table of packages needed to be installed (will be returned)
local to_install = {}
-- find out available versions of 'pkg' and insert them into manifest
if not force_no_download then
local versions, err = package.retrieve_versions(pkg, manifest, suppress_printing)
if not versions then return nil, err end
for _, version in pairs(versions) do
table.insert(manifest, version)
end
end
-- find candidates & sort them
local candidates_to_install = find_packages(pkg, manifest)
if #candidates_to_install == 0 then
return nil, "No suitable candidate for '" .. pkg .. "' found."
end
candidates_to_install = sort_by_versions(candidates_to_install)
for _, pkg in pairs(candidates_to_install) do
--[[ for future debugging:
print(' candidate: '.. pkg.name..'-'..pkg.version)
print(' installed: ', utils.table_tostring(installed))
print(' tmp_installed: ', utils.table_tostring(tmp_installed))
print(' to_install: ', utils.table_tostring(to_install))
print(' -is installed: ', is_installed(pkg.name, tmp_installed, pkg_constraint))
--]]
-- if there's an error from the previous candidate, print the reason for trying another one
if not suppress_printing and err then print(" - trying another candidate due to: " .. err) end
-- clear the state from the previous candidate
pkg_is_installed, err = false, nil
-- check whether this package has already been added to 'tmp_installed' by another of its candidates
pkg_is_installed, err = is_installed(pkg.name, tmp_installed, pkg_constraint)
if pkg_is_installed then break end
-- preserve information about the 'scm' version, because pkg.version
-- will be rewritten by information taken from pkg's dist.info file
local was_scm_version = (pkg.version == "scm")
-- Try to obtain cached dependency information from the dependency manifest
if dependency_manifest[pkg.name .. "-" .. pkg.version] and cfg.dep_cache then
pkg = dependency_manifest[pkg.name .. "-" .. pkg.version]
else
-- download info about the package if not already downloaded and downloading not prohibited
if not (pkg.download_dir or force_no_download) then
local path_or_err
pkg, path_or_err = package.retrieve_pkg_info(pkg, deploy_dir, suppress_printing)
if not pkg then
err = "Error when resolving dependencies: " .. path_or_err
else
-- set path to downloaded package - used to indicate that the
-- package was already downloaded, to delete unused but downloaded
-- packages and also to install choosen packages
pkg.download_dir = path_or_err
end
end
end
if pkg and was_scm_version then pkg.was_scm_version = true end
-- check arch & type
if not err then
if not (pkg.arch == "Universal" or pkg.arch == cfg.arch) or
not (pkg.type == "all" or pkg.type == "source" or pkg.type == cfg.type) then
err = "Package '" .. pkg_full_name(pkg.name, pkg.version) .. "' doesn't have required arch and type."
end
end
-- checks for conflicts with other installed (or previously selected) packages
if not err then
for _, installed_pkg in pairs(tmp_installed) do
err = packages_conflicts(pkg, installed_pkg)
if err then break end
end
end
-- if pkg passed all of the above tests
if not err then
-- check if pkg's dependencies are satisfied
if pkg.depends then
-- insert pkg into the stack of circular dependencies detection
table.insert(dependency_parents, pkg.name)
-- extract all OS specific dependencies of pkg
pkg.depends = extract_os_specific_depends(pkg.depends)
-- for all dependencies of pkg
for _, depend in pairs(pkg.depends) do
local dep_name = split_name_constraint(depend)
-- detect circular dependencies using 'dependency_parents'
local is_circular_dependency = false
for _, parent in pairs(dependency_parents) do
if dep_name == parent then
is_circular_dependency = true
break
end
end
-- if circular dependencies not detected
if not is_circular_dependency then
-- recursively call this function on the candidates of this pkg's dependency
local depends_to_install, dep_err = get_packages_to_install(depend, installed, manifest, dependency_manifest, force_no_download, suppress_printing, deploy_dir, dependency_parents, tmp_installed)
-- if any suitable dependency packages were found, insert them to the 'to_install' table
if depends_to_install then
for _, depend_to_install in pairs(depends_to_install) do
-- add some meta information
if not depend_to_install.selected_by then
depend_to_install.selected_by = pkg.name .. "-" .. pkg.version
end
table.insert(to_install, depend_to_install)
table.insert(tmp_installed, depend_to_install)
table.insert(installed, depend_to_install)
end
else
err = "Error getting dependency of '" .. pkg_full_name(pkg.name, pkg.version) .. "': " .. dep_err
break
end
-- if circular dependencies detected
else
err = "Error getting dependency of '" .. pkg_full_name(pkg.name, pkg.version) .. "': '" .. dep_name .. "' is a circular dependency."
break
end
end
-- remove last package from the stack of circular dependencies detection
table.remove(dependency_parents)
end
-- if no error occured
if not err then
-- add pkg and it's provides to the fake table of installed packages, with
-- property 'selected' set, indicating that the package isn't
-- really installed in the system, just selected to be installed (this is used e.g. in error messages)
pkg.selected = true
table.insert(tmp_installed, pkg)
if pkg.provides then
for _, provided_pkg in pairs(get_provides(pkg)) do
provided_pkg.selected = true
table.insert(tmp_installed, provided_pkg)
end
end
-- add pkg to the table of packages to install
table.insert(to_install, pkg)
-- if some error occured
else
-- delete the downloaded package
if pkg.download_dir and not cfg.debug then sys.delete(pkg.download_dir) end
-- set tables of 'packages to install' and 'installed packages' to their original state
to_install = {}
tmp_installed = utils.deepcopy(installed)
-- add provided packages to installed ones
for _, installed_pkg in pairs(tmp_installed) do
for _, pkg in pairs(get_provides(installed_pkg)) do
table.insert(tmp_installed, pkg)
end
end
end
-- if error occured
else
-- delete the downloaded package
if pkg and pkg.download_dir and not cfg.debug then sys.delete(pkg.download_dir) end
-- if pkg is already installed, skip checking its other candidates
if pkg_is_installed then break end
end
end
-- if package is not installed and no suitable candidates were found, return the last error
if #to_install == 0 and not pkg_is_installed then
return nil, err
else
return to_install
end
end
-- Resolve dependencies and return all packages needed in order to install
-- 'packages' into the system with already 'installed' packages, using 'manifest'.
-- Also return the table of the dependencies determined during the process
-- as the second return value.
--
-- Optional argument 'dependency_manifest' is a table of dependencies examined
-- from previous installations etc. It can be used to speed-up the dependency
-- resolving procedure for example.
--
-- Optional argument 'deploy_dir' is used as a temporary place to place the
-- downloaded packages into.
--
-- When optional 'force_no_download' parameter is set to true, then information
-- about packages won't be downloaded during dependency resolving, assuming that
-- entries in manifest are complete.
--
-- When optional 'suppress_printing' parameter is set to true, then messages
-- for the user won't be printed during dependency resolving.
function get_depends(packages, installed, manifest, dependency_manifest, deploy_dir, force_no_download, suppress_printing)
if not packages then return {} end
manifest = manifest or mf.get_manifest()
dependency_manifest = dependency_manifest or {}
deploy_dir = deploy_dir or cfg.root_dir
force_no_download = force_no_download or false
suppress_printing = suppress_printing or false
if type(packages) == "string" then packages = {packages} end
assert(type(packages) == "table", "depends.get_depends: Argument 'packages' is not a table or string.")
assert(type(installed) == "table", "depends.get_depends: Argument 'installed' is not a table.")
assert(type(manifest) == "table", "depends.get_depends: Argument 'manifest' is not a table.")
assert(type(dependency_manifest) == "table", "depends.get_depends: Argument 'dependency_manifest' is not a table.")
assert(type(deploy_dir) == "string", "depends.get_depends: Argument 'deploy_dir' is not a string.")
assert(type(force_no_download) == "boolean", "depends.get_depends: Argument 'force_no_download' is not a boolean.")
assert(type(suppress_printing) == "boolean", "depends.get_depends: Argument 'suppress_printing' is not a boolean.")
deploy_dir = sys.abs_path(deploy_dir)
local tmp_installed = utils.deepcopy(installed)
-- add provided packages to installed ones
for _, installed_pkg in pairs(tmp_installed) do
for _, pkg in pairs(get_provides(installed_pkg)) do
table.insert(tmp_installed, pkg)
end
end
-- If 'pkg' contains valid (architecture specific) path separator,
-- it is treated like a path to already downloaded package and
-- we assume that user wants to use this specific version of the
-- module to be installed. Hence, we will add information about
-- this version into the manifest and also remove references to
-- any other versions of this module from the manifest. This will
-- enforce the version of the module required by the user.
for k, pkg in pairs(packages) do
if pkg:find(sys.path_separator()) then
local pkg_dir = sys.abs_path(pkg)
local pkg_info, err = mf.load_distinfo(sys.make_path(pkg_dir, "dist.info"))
if not pkg_info then return nil, err end
-- add information about location of the package, also to prevent downloading it again
pkg_info.download_dir = pkg_dir
-- mark package to skip deleting its directory after installation
pkg_info.preserve_pkg_dir = true
-- set default arch/type if not explicitly stated and package is of source type
if package.is_source_type(pkg_dir) then
pkg_info = package.ensure_source_arch_and_type(pkg_info)
elseif not (pkg_info.arch and pkg_info.type) then
return nil, pkg_dir .. ": binary package missing arch or type in 'dist.info'."
end
-- update manifest
manifest = utils.filter(manifest, function(p) return p.name ~= pkg_info.name and true end)
table.insert(manifest, pkg_info)
-- update packages to install
pkg = pkg_info.name .. "-" .. pkg_info.version
packages[k] = pkg
end
end
local to_install = {}
-- get packages needed to satisfy the dependencies
for _, pkg in pairs(packages) do
local needed_to_install, err = get_packages_to_install(pkg, tmp_installed, manifest, dependency_manifest, force_no_download, suppress_printing, deploy_dir)
-- if everything's fine
if needed_to_install then
for _, needed_pkg in pairs(needed_to_install) do
-- TODO: why not to use 'installed' instead of 'tmp_installed'?
-- It's because provides aren't searched for by find()
-- function inside the update_dependency_manifest().
dependency_manifest = update_dependency_manifest(needed_pkg, tmp_installed, needed_to_install, dependency_manifest)
table.insert(to_install, needed_pkg)
table.insert(tmp_installed, needed_pkg)
-- add provides of needed_pkg to installed ones
for _, provided_pkg in pairs(get_provides(needed_pkg)) do
-- copy 'selected' property
provided_pkg.selected = needed_pkg.selected
table.insert(tmp_installed, provided_pkg)
end
end
-- if error occured
else
-- delete already downloaded packages
for _, pkg in pairs(to_install) do
if pkg.download_dir and not cfg.debug then sys.delete(pkg.download_dir) end
end
return nil, "Cannot resolve dependencies for '" .. pkg .. "': ".. err
end
end
return to_install, dependency_manifest
end
-- Return table of packages provided by specified package (from it's 'provides' field)
function get_provides(package)
assert(type(package) == "table", "depends.get_provides: Argument 'package' is not a table.")
if not package.provides then return {} end
local provided = {}
for _, provided_name in pairs(package.provides) do
local pkg = {}
pkg.name, pkg.version = split_name_constraint(provided_name)
pkg.type = package.type
pkg.arch = package.arch
pkg.provided = package.name .. "-" .. package.version
table.insert(provided, pkg)
end
return provided
end
-- Return package name and version constraint from full package version constraint specification
-- E. g.:
-- for 'luaexpat-1.2.3' return: 'luaexpat' , '1.2.3'
-- for 'luajit >= 1.2' return: 'luajit' , '>=1.2'
function split_name_constraint(version_constraint)
assert(type(version_constraint) == "string", "depends.split_name_constraint: Argument 'version_constraint' is not a string.")
local split = version_constraint:find("[%s=~<>-]+%d") or version_constraint:find("[%s=~<>-]+scm")
if split then
return version_constraint:sub(1, split - 1), version_constraint:sub(split):gsub("[%s-]", "")
else
return version_constraint, nil
end
end
-- Return only packages that can be installed on the specified architecture and type
function filter_packages_by_arch_and_type(packages, req_arch, req_type)
assert(type(packages) == "table", "depends.filter_packages_by_arch_and_type: Argument 'packages' is not a table.")
assert(type(req_arch) == "string", "depends.filter_packages_by_arch_and_type: Argument 'req_arch' is not a string.")
assert(type(req_type) == "string", "depends.filter_packages_by_arch_and_type: Argument 'pkg_type' is not a string.")
return utils.filter(packages,
function (pkg)
return (pkg.arch == "Universal" or pkg.arch == req_arch) and
(pkg.type == "all" or pkg.type == "source" or pkg.type == req_type)
end)
end
-- Return only packages that contain one of the specified strings in their 'name-version'.
-- Case is ignored. If no strings are specified, return all the packages.
-- Argument 'search_in_desc' specifies if search also in description of packages.
function filter_packages_by_strings(packages, strings, search_in_desc)
if type(strings) == "string" then strings = {strings} end
assert(type(packages) == "table", "depends.filter_packages_by_strings: Argument 'packages' is not a table.")
assert(type(strings) == "table", "depends.filter_packages_by_strings: Argument 'strings' is not a string or table.")
if #strings ~= 0 then
return utils.filter(packages,
function (pkg)
for _,str in pairs(strings) do
local name = pkg.name .. "-" .. pkg.version
if search_in_desc then
name = name .. " " .. (pkg.desc or "")
end
if string.find(string.lower(name), string.lower(str), 1 ,true) ~= nil then return true end
end
end)
else
return packages
end
end
-- Return full package name and version string (e.g. 'luajit-2.0'). When version
-- is nil or '' then return only name (e.g. 'luajit') and when name is nil or ''
-- then return '<unknown>'. Optional 'was_scm_version' argument is a boolean,
-- stating whether the package was originally selected for installation as a 'scm' version.
function pkg_full_name(name, version, was_scm_version)
name = name or ""
version = version or ""
was_scm_version = was_scm_version or false
if type(version) == "number" then version = tostring(version) end
assert(type(name) == "string", "depends.pkg_full_name: Argument 'name' is not a string.")
assert(type(version) == "string", "depends.pkg_full_name: Argument 'version' is not a string.")
if was_scm_version then version = version .. " [scm version]" end
if name == "" then
return "<unknown>"
else
return name .. ((version ~= "") and "-" .. version or "")
end
end
-- Return table of packages, sorted descendingly by versions (newer ones are moved to the top).
function sort_by_versions(packages)
assert(type(packages) == "table", "depends.sort_by_versions: Argument 'packages' is not a table.")
return utils.sort(packages, function (a, b) return compare_versions(a.version, b.version) end)
end
-- Return table of packages, sorted alphabetically by name and then descendingly by version.
function sort_by_names(packages)
assert(type(packages) == "table", "depends.sort_by_names: Argument 'packages' is not a table.")
return utils.sort(packages, function (a, b)
if a.name == b.name then
return compare_versions(a.version, b.version)
else
return a.name < b.name
end
end)
end
-- Return if version satisfies the specified constraint
function satisfies_constraint(version, constraint)
assert(type(version) == "string", "depends.satisfies_constraint: Argument 'version' is not a string.")
assert(type(constraint) == "string", "depends.satisfies_constraint: Argument 'constraint' is not a string.")
return const.constraint_satisfied(version, constraint)
end
-- For package versions, return whether: 'version_a' > 'version_b'
function compare_versions(version_a, version_b)
assert(type(version_a) == "string", "depends.compare_versions: Argument 'version_a' is not a string.")
assert(type(version_b) == "string", "depends.compare_versions: Argument 'version_b' is not a string.")
return const.compareVersions(version_a, version_b)
end
-- Returns 'dep_manifest' updated with information about the 'pkg'.
-- 'installed' is table with installed packages
-- 'to_install' is table with packages that are selected for installation
-- Packages satisfying the dependencies will be searched for in these two tables.
function update_dependency_manifest(pkg, installed, to_install, dep_manifest)
dep_manifest = dep_manifest or {}
assert(type(pkg) == "table", "depends.update_dependency_manifest: Argument 'pkg' is not a table.")
assert(type(installed) == "table", "depends.update_dependency_manifest: Argument 'installed' is not a table.")
assert(type(to_install) == "table", "depends.update_dependency_manifest: Argument 'to_install' is not a table.")
assert(type(dep_manifest) == "table", "depends.update_dependency_manifest: Argument 'dep_manifest' is not a table.")
local name_ver = pkg.name .. "-" .. (pkg.was_scm_version and "scm" or pkg.version)
-- add to manifest
if not dep_manifest[name_ver] then
dep_manifest[name_ver] = {}
dep_manifest[name_ver].name = pkg.name
dep_manifest[name_ver].version = pkg.version
dep_manifest[name_ver].was_scm_version = pkg.was_scm_version
dep_manifest[name_ver].arch = pkg.arch
dep_manifest[name_ver].type = pkg.type
dep_manifest[name_ver].path = pkg.path
dep_manifest[name_ver].depends = pkg.depends
dep_manifest[name_ver].conflicts = pkg.conflicts
dep_manifest[name_ver].provides = pkg.provides
dep_manifest[name_ver].license = pkg.license
dep_manifest[name_ver].desc = pkg.desc
dep_manifest[name_ver].url = pkg.url
dep_manifest[name_ver].author = pkg.author
dep_manifest[name_ver].maintainer = pkg.maintainer
-- add information which dependency is satisfied by which package
if pkg.depends then
-- TODO: Won't it be better to add OS-specific 'satisfied_by' metadata in a format like OS-specific 'depends' ?
local all_deps = extract_os_specific_depends(pkg.depends)
dep_manifest[name_ver].satisfied_by = {}
for _, depend in pairs(all_deps) do
-- find package satisfying the dependency
local satisfying = find_packages(depend, installed)[1] or find_packages(depend, to_install)[1]
satisfying = satisfying.name .. "-" .. satisfying.version
dep_manifest[name_ver].satisfied_by[depend] = satisfying
-- check whether the satisfying package isn't provided by other one
local provided_by = utils.filter(installed, function(pkg)
return pkg.provides and utils.contains(pkg.provides, satisfying)
end)
if #provided_by == 0 then
provided_by = utils.filter(to_install, function(pkg)
return pkg.provides and utils.contains(pkg.provides, satisfying)
end)
end
if #provided_by ~= 0 then
if not dep_manifest[name_ver].satisfying_provided_by then
dep_manifest[name_ver].satisfying_provided_by = {}
end
dep_manifest[name_ver].satisfying_provided_by[satisfying] = provided_by[1].name .. "-" .. provided_by[1].version
end
end
end
end
return dep_manifest
end

View File

@ -1,306 +0,0 @@
-- Encapsulated Git functionality
module ("dist.git", package.seeall)
require "git"
local sys = require "dist.sys"
local cfg = require "dist.config"
-- Clone the repository from url to dest_dir
function clone(repository_url, dest_dir, depth, branch)
assert(type(repository_url) == "string", "git.clone: Argument 'repository_url' is not a string.")
assert(type(dest_dir) == "string", "git.clone: Argument 'dest_dir' is not a string.")
dest_dir = sys.abs_path(dest_dir)
local command = "git clone " .. repository_url
if depth then
assert(type(depth) == "number", "git.clone: Argument 'depth' is not a number.")
command = command .. " --depth " .. depth
end
if branch then
assert(type(branch) == "string", "git.clone: Argument 'branch' is not a string.")
command = command .. " -b " .. branch
end
command = command .. " " .. sys.quote(dest_dir)
if sys.exists(dest_dir) then sys.delete(dest_dir) end
sys.make_dir(dest_dir)
-- change the current working directory to dest_dir
local prev_current_dir = sys.current_dir()
sys.change_dir(dest_dir)
-- execute git clone
if not cfg.debug then command = command .. " -q " end
local ok, err = sys.exec(command)
-- change the current working directory back
sys.change_dir(prev_current_dir)
return ok, err
end
-- Return table of all refs of the remote repository at the 'git_url'. Ref_type can be "tags" or "heads".
local function get_remote_refs(git_url, ref_type)
assert(type(git_url) == "string", "git.get_remote_refs: Argument 'git_url' is not a string.")
assert(type(ref_type) == "string", "git.get_remote_refs: Argument 'ref_type' is not a string.")
assert(ref_type == "tags" or ref_type == "heads", "git.get_remote_refs: Argument 'ref_type' is not \"tags\" or \"heads\".")
local refs = {}
local ok, refs_or_err = pcall(git.protocol.remotes, git_url)
if not ok then return nil, "Error getting refs of the remote repository '" .. git_url .. "': " .. refs_or_err end
for ref, sha in pairs(refs_or_err) do
if ref:match("%S+/" .. ref_type .. "/%S+") and not ref:match("%^{}") then
table.insert(refs, ref:match("%S+/" .. ref_type .. "/(%S+)"))
end
end
return refs
end
-- Return table of all tags of the repository at the 'git_url'
function get_remote_tags(git_url)
return get_remote_refs(git_url, "tags")
end
-- Return table of all branches of the repository at the 'git_url'
function get_remote_branches(git_url)
return get_remote_refs(git_url, "heads")
end
-- Checkout specified ref in specified git_repo_dir
function checkout_ref(ref, git_repo_dir, orphaned)
git_repo_dir = git_repo_dir or sys.current_dir()
orphaned = orphaned or false
assert(type(ref) == "string", "git.checkout_ref: Argument 'ref' is not a string.")
assert(type(git_repo_dir) == "string", "git.checkout_ref: Argument 'git_repo_dir' is not a string.")
assert(type(orphaned) == "boolean", "git.checkout_ref: Argument 'orphaned' is not a boolean.")
git_repo_dir = sys.abs_path(git_repo_dir)
local command = "git checkout "
if orphaned then command = command .. " --orphan " end
command = command .. " " .. ref .. " -f"
if not cfg.debug then command = command .. " -q " end
local ok, err
if git_repo_dir ~= sys.current_dir() then
local prev_current_dir = sys.current_dir()
sys.change_dir(git_repo_dir)
ok, err = sys.exec(command)
sys.change_dir(prev_current_dir)
else
ok, err = sys.exec(command)
end
return ok, err
end
-- Checkout specified sha in specified git_repo_dir
function checkout_sha(sha, git_repo_dir)
git_repo_dir = git_repo_dir or sys.current_dir()
assert(type(sha) == "string", "git.checkout_sha: Argument 'sha' is not a string.")
assert(type(git_repo_dir) == "string", "git.checkout_sha: Argument 'git_repo_dir' is not a string.")
git_repo_dir = sys.abs_path(git_repo_dir)
local dir_changed, prev_current_dir
if git_repo_dir ~= sys.current_dir() then
prev_current_dir = sys.current_dir()
sys.change_dir(git_repo_dir)
dir_changed = true
end
local ok, repo_or_err = pcall(git.repo.open, git_repo_dir)
if not ok then return nil, "Error when opening the git repository '" .. git_repo_dir .. "': " .. repo_or_err end
local err
ok, err = pcall(repo_or_err.checkout, repo_or_err, sha, git_repo_dir)
if not ok then return nil, "Error when checking out the sha '" .. sha .. "' in the git repository '" .. git_repo_dir .. "': " .. err end
repo_or_err:close()
if dir_changed then sys.change_dir(prev_current_dir) end
return true
end
-- Create an empty git repository in given directory.
function init(dir)
dir = dir or sys.current_dir()
assert(type(dir) == "string", "git.init: Argument 'dir' is not a string.")
dir = sys.abs_path(dir)
-- create the 'dir' first, since it causes 'git init' to fail on Windows
-- when the parent directory of 'dir' doesn't exist
local ok, err = sys.make_dir(dir)
if not ok then return nil, err end
local command = "git init " .. sys.quote(dir)
if not cfg.debug then command = command .. " -q " end
return sys.exec(command)
end
-- Add all files in the 'repo_dir' to the git index. The 'repo_dir' must be
-- in the initialized git repository.
function add_all(repo_dir)
repo_dir = repo_dir or sys.current_dir()
assert(type(repo_dir) == "string", "git.add_all: Argument 'repo_dir' is not a string.")
repo_dir = sys.abs_path(repo_dir)
local ok, prev_dir, msg
ok, prev_dir = sys.change_dir(repo_dir);
if not ok then return nil, err end
ok, msg = sys.exec("git add -A -f " .. sys.quote(repo_dir))
sys.change_dir(prev_dir)
return ok, msg
end
-- Commit all indexed files in 'repo_dir' with the given commit 'message'.
-- The 'repo_dir' must be in the initialized git repository.
function commit(message, repo_dir)
repo_dir = repo_dir or sys.current_dir()
message = message or "commit by luadist-git"
assert(type(message) == "string", "git.commit: Argument 'message' is not a string.")
assert(type(repo_dir) == "string", "git.commit: Argument 'repo_dir' is not a string.")
repo_dir = sys.abs_path(repo_dir)
local ok, prev_dir, msg
ok, prev_dir = sys.change_dir(repo_dir);
if not ok then return nil, err end
local command = "git commit -m " .. sys.quote(message)
if not cfg.debug then command = command .. " -q " end
ok, msg = sys.exec(command)
sys.change_dir(prev_dir)
return ok, msg
end
-- Rename branch 'old_name' to 'new_name'. -- The 'repo_dir' must be
-- in the initialized git repository and the branch 'new_name' must
-- not already exist in that repository.
function rename_branch(old_name, new_name, repo_dir)
repo_dir = repo_dir or sys.current_dir()
assert(type(old_name) == "string", "git.rename_branch: Argument 'old_name' is not a string.")
assert(type(new_name) == "string", "git.rename_branch: Argument 'new_name' is not a string.")
assert(type(repo_dir) == "string", "git.rename_branch: Argument 'repo_dir' is not a string.")
repo_dir = sys.abs_path(repo_dir)
local ok, prev_dir, msg
ok, prev_dir = sys.change_dir(repo_dir);
if not ok then return nil, err end
ok, msg = sys.exec("git branch -m " .. old_name .. " " .. new_name)
sys.change_dir(prev_dir)
return ok, msg
end
-- Push the ref 'ref_name' from the 'repo_dir' to the remote git
-- repository 'git_repo_url'. If 'all_tags' is set to true, all tags
-- will be pushed, in addition to the explicitly given ref.
-- If 'delete' is set to 'true' then the explicitly given remote ref
-- will be deleted, not pushed.
function push_ref(repo_dir, ref_name, git_repo_url, all_tags, delete)
repo_dir = repo_dir or sys.current_dir()
all_tags = all_tags or false
delete = delete or false
assert(type(repo_dir) == "string", "git.push_ref: Argument 'repo_dir' is not a string.")
assert(type(git_repo_url) == "string", "git.push_ref: Argument 'git_repo_url' is not a string.")
assert(type(ref_name) == "string", "git.push_ref: Argument 'ref_name' is not a string.")
assert(type(all_tags) == "boolean", "git.push_ref: Argument 'all_tags' is not a boolean.")
assert(type(delete) == "boolean", "git.push_ref: Argument 'delete' is not a boolean.")
repo_dir = sys.abs_path(repo_dir)
local ok, prev_dir, msg
ok, prev_dir = sys.change_dir(repo_dir);
if not ok then return nil, err end
local command = "git push " .. git_repo_url
if all_tags then command = command .. " --tags " end
if delete then command = command .. " --delete " end
command = command .. " " .. ref_name .. " -f "
if not cfg.debug then command = command .. " -q " end
ok, msg = sys.exec(command)
sys.change_dir(prev_dir)
return ok, msg
end
-- Creates the tag 'tag_name' in given 'repo_dir', which must be
-- in the initialized git repository
function create_tag(repo_dir, tag_name)
repo_dir = repo_dir or sys.current_dir()
assert(type(repo_dir) == "string", "git.create_tag: Argument 'repo_dir' is not a string.")
assert(type(tag_name) == "string", "git.create_tag: Argument 'tag_name' is not a string.")
repo_dir = sys.abs_path(repo_dir)
local ok, prev_dir, msg
ok, prev_dir = sys.change_dir(repo_dir);
if not ok then return nil, err end
ok, msg = sys.exec("git tag " .. tag_name .. " -f ")
sys.change_dir(prev_dir)
return ok, msg
end
-- Fetch given 'ref_name' from the remote 'git_repo_url' to the local repository
-- 'repo_dir' and return its sha. 'ref_type' can be "tag" or "head".
local function fetch_ref(repo_dir, git_repo_url, ref_name, ref_type)
repo_dir = repo_dir or sys.current_dir()
assert(type(repo_dir) == "string", "git.fetch_ref: Argument 'repo_dir' is not a string.")
assert(type(git_repo_url) == "string", "git.fetch_ref: Argument 'git_repo_url' is not a string.")
assert(type(ref_name) == "string", "git.fetch_ref: Argument 'ref_name' is not a string.")
assert(type(ref_type) == "string", "git.fetch_ref: Argument 'ref_type' is not a string.")
assert(ref_type == "tag" or ref_type == "head", "git.get_remote_refs: Argument 'ref_type' is not \"tag\" or \"head\".")
repo_dir = sys.abs_path(repo_dir)
local refstring = "refs/" .. ref_type .. "s/" .. ref_name
local suppress_fetch_progress = not cfg.debug
local ok, repo_or_err = pcall(git.repo.open, repo_dir)
if not ok then return nil, "Error when opening the git repository '" .. repo_dir .. "': " .. repo_or_err end
local ok, pack_or_err, sha = pcall(git.protocol.fetch, git_repo_url, repo_or_err, refstring, suppress_fetch_progress)
if not ok then return nil, "Error when fetching ref '" .. refstring .. "' from git repository '" .. git_repo_url .. "': " .. pack_or_err end
repo_or_err:close()
pack_or_err:close()
return sha
end
-- Fetch given 'tag_name' from the remote 'git_repo_url' to the local repository
-- 'repo_dir' and save it as a tag with the same 'tag_name'.
function fetch_tag(repo_dir, git_repo_url, tag_name)
return fetch_ref(repo_dir, git_repo_url, tag_name, "tag")
end
-- Fetch given 'branch_name' from the remote 'git_repo_url' to the local repository
-- 'repo_dir' and save it as a branch with the same 'branch_name'.
function fetch_branch(repo_dir, git_repo_url, branch_name)
return fetch_ref(repo_dir, git_repo_url, branch_name, "head")
end
-- Create the git repository and return the repo object (which can be used in checkout_sha etc.)
-- If the 'dir' exists, it's deleted prior to creating the git repository.
function create_repo(dir)
assert(type(dir) == "string", "git.create_repo: Argument 'dir' is not a string.")
if sys.exists(dir) then sys.delete(dir) end
local ok, repo_or_err = pcall(git.repo.create, dir)
if not ok then return nil, "Error when creating the git repository '" .. dir .. "': " .. repo_or_err end
repo_or_err:close()
return true
end

View File

@ -1,349 +0,0 @@
-- main API of LuaDist
module ("dist", package.seeall)
local cfg = require "dist.config"
local depends = require "dist.depends"
local git = require "dist.git"
local sys = require "dist.sys"
local package = require "dist.package"
local mf = require "dist.manifest"
local utils = require "dist.utils"
-- Return the deployment directory.
function get_deploy_dir()
return sys.abs_path(cfg.root_dir)
end
-- Return packages deployed in 'deploy_dir' also with their provides.
function get_deployed(deploy_dir)
deploy_dir = deploy_dir or cfg.root_dir
assert(type(deploy_dir) == "string", "dist.get_deployed: Argument 'deploy_dir' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
local deployed = depends.get_installed(deploy_dir)
local provided = {}
for _, pkg in pairs(deployed) do
for _, provided_pkg in pairs(depends.get_provides(pkg)) do
provided_pkg.provided_by = pkg.name .. "-" .. pkg.version
table.insert(provided, provided_pkg)
end
end
for _, provided_pkg in pairs(provided) do
table.insert(deployed, provided_pkg)
end
deployed = depends.sort_by_names(deployed)
return deployed
end
-- Download new 'manifest_file' from repository and returns it.
-- Return nil and error message on error.
function update_manifest(deploy_dir)
deploy_dir = deploy_dir or cfg.root_dir
assert(type(deploy_dir) == "string", "dist.update_manifest: Argument 'deploy_dir' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
-- TODO: use 'deploy_dir' argument in manifest functions
-- retrieve the new manifest (forcing no cache use)
local manifest, err = mf.get_manifest(nil, true)
if manifest then
return manifest
else
return nil, err
end
end
-- Install 'package_names' to 'deploy_dir', using optional CMake 'variables'.
function install(package_names, deploy_dir, variables)
if not package_names then return true end
deploy_dir = deploy_dir or cfg.root_dir
if type(package_names) == "string" then package_names = {package_names} end
assert(type(package_names) == "table", "dist.install: Argument 'package_names' is not a table or string.")
assert(type(deploy_dir) == "string", "dist.install: Argument 'deploy_dir' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
-- find installed packages
local installed = depends.get_installed(deploy_dir)
-- get manifest
local manifest, err = mf.get_manifest()
if not manifest then return nil, "Error getting manifest: " .. err end
-- get dependency manifest
-- TODO: Is it good that dep_manifest is deploy_dir-specific?
-- Probably it'd be better not to be specific, but then there're
-- problems with 'provides'. E.g. What to do if there's a module
-- installed, that is provided by two different modules in two deploy_dirs?
local dep_manifest_file = sys.abs_path(sys.make_path(deploy_dir, cfg.dep_cache_file))
local dep_manifest, status = {}
if sys.exists(dep_manifest_file) and not utils.cache_timeout_expired(cfg.cache_timeout, dep_manifest_file) then
status, dep_manifest = mf.load_manifest(dep_manifest_file)
if not dep_manifest then return nil, status end
end
-- resolve dependencies
local dependencies, dep_manifest_or_err = depends.get_depends(package_names, installed, manifest, dep_manifest, deploy_dir, false, false)
if not dependencies then return nil, dep_manifest_or_err end
if #dependencies == 0 then return nil, "No packages to install." end
-- save updated dependency manifest
local ok, err = sys.make_dir(sys.parent_dir(dep_manifest_file))
if not ok then return nil, err end
ok, err = mf.save_manifest(dep_manifest_or_err, dep_manifest_file)
if not ok then return nil, err end
-- fetch the packages from repository
local fetched_pkgs = {}
for _, pkg in pairs(dependencies) do
local fetched_pkg, err = package.fetch_pkg(pkg, sys.make_path(deploy_dir, cfg.temp_dir))
if not fetched_pkg then return nil, err end
table.insert(fetched_pkgs, fetched_pkg)
end
-- install fetched packages
for _, pkg in pairs(fetched_pkgs) do
local ok, err = package.install_pkg(pkg.download_dir, deploy_dir, variables, pkg.preserve_pkg_dir)
if not ok then return nil, err end
end
return true
end
-- Manually deploy packages from 'package_dirs' to 'deploy_dir', using optional
-- CMake 'variables'. The 'package_dirs' are preserved (will not be deleted).
function make(deploy_dir, package_dirs, variables)
deploy_dir = deploy_dir or cfg.root_dir
package_dirs = package_dirs or {}
assert(type(deploy_dir) == "string", "dist.make: Argument 'deploy_dir' is not a string.")
assert(type(package_dirs) == "table", "dist.make: Argument 'package_dirs' is not a table.")
deploy_dir = sys.abs_path(deploy_dir)
for _, dir in pairs(package_dirs) do
local ok, err = package.install_pkg(sys.abs_path(dir), deploy_dir, variables, true)
if not ok then return nil, err end
end
return true
end
-- Remove 'package_names' from 'deploy_dir' and return the number of removed
-- packages.
function remove(package_names, deploy_dir)
deploy_dir = deploy_dir or cfg.root_dir
if type(package_names) == "string" then package_names = {package_names} end
assert(type(package_names) == "table", "dist.remove: Argument 'package_names' is not a string or table.")
assert(type(deploy_dir) == "string", "dist.remove: Argument 'deploy_dir' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
local pkgs_to_remove = {}
local installed = depends.get_installed(deploy_dir)
-- find packages to remove
if #package_names == 0 then
pkgs_to_remove = installed
else
pkgs_to_remove = depends.find_packages(package_names, installed)
end
-- remove them
for _, pkg in pairs(pkgs_to_remove) do
local pkg_distinfo_dir = sys.make_path(cfg.distinfos_dir, pkg.name .. "-" .. pkg.version)
local ok, err = package.remove_pkg(pkg_distinfo_dir, deploy_dir)
if not ok then return nil, err end
end
return #pkgs_to_remove
end
-- Download 'pkg_names' to 'fetch_dir' and return the table of their directories.
function fetch(pkg_names, fetch_dir)
fetch_dir = fetch_dir or sys.current_dir()
assert(type(pkg_names) == "table", "dist.fetch: Argument 'pkg_names' is not a string or table.")
assert(type(fetch_dir) == "string", "dist.fetch: Argument 'fetch_dir' is not a string.")
fetch_dir = sys.abs_path(fetch_dir)
local manifest = mf.get_manifest()
local pkgs_to_fetch = {}
for _, pkg_name in pairs(pkg_names) do
-- retrieve available versions
local versions, err = package.retrieve_versions(pkg_name, manifest)
if not versions then return nil, err end
for _, version in pairs(versions) do
table.insert(manifest, version)
end
local packages = depends.find_packages(pkg_name, manifest)
if #packages == 0 then return nil, "No packages found for '" .. pkg_name .. "'." end
packages = depends.sort_by_versions(packages)
table.insert(pkgs_to_fetch, packages[1])
end
local fetched_dirs = {}
for _, pkg in pairs(pkgs_to_fetch) do
local fetched_pkg, err = package.fetch_pkg(pkg, fetch_dir)
if not fetched_pkg then return nil, err end
table.insert(fetched_dirs, fetched_pkg.download_dir)
end
return fetched_dirs
end
-- Upload binary version of given modules installed in the specified
-- 'deploy_dir' to the repository specified by provided base url.
-- Return the number of uploaded packages.
--
-- Organization of uploaded modules and their repositories is subject
-- to the following conventions:
-- - destination repository is: 'DEST_GIT_BASE_URL/MODULE_NAME'
-- - module will be uploaded to the branch: 'ARCH-TYPE' according
-- to the arch and type of the user's machine
-- - the module will be tagged as: 'VERSION-ARCH-TYPE' (if the tag already
-- exists, it will be overwritten)
--
-- E.g. assume that the module 'lua-5.1.4' is installed on the 32bit Linux
-- system (Linux-i686). When this function is called with the module name
-- 'lua' and base url 'git@github.com:LuaDist', then the binary version
-- of the module 'lua', that is installed on the machine, will be uploaded
-- to the branch 'Linux-i686' of the repository 'git@github.com:LuaDist/lua.git'
-- and tagged as '5.1.4-Linux-i686'.
function upload_modules(deploy_dir, module_names, dest_git_base_url)
deploy_dir = deploy_dir or cfg.root_dir
if type(module_names) == "string" then module_names = {module_names} end
assert(type(deploy_dir) == "string", "dist.upload_module: Argument 'deploy_dir' is not a string.")
assert(type(module_names) == "table", "dist.upload_module: Argument 'module_name' is not a string or table.")
assert(type(dest_git_base_url) == "string", "dist.upload_module: Argument 'dest_git_base_url' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
local modules_to_upload = {}
local installed = depends.get_installed(deploy_dir)
-- find modules to upload
if #module_names == 0 then
modules_to_upload = installed
else
modules_to_upload = depends.find_packages(module_names, installed)
end
for _, installed_module in pairs(modules_to_upload) do
-- set names
local branch_name = cfg.arch .. "-" .. cfg.type
local tag_name = installed_module.version .. "-" .. branch_name
local full_name = installed_module.name .. "-" .. tag_name
local tmp_dir = sys.make_path(deploy_dir, cfg.temp_dir, full_name .. "-to-upload")
local dest_git_url = dest_git_base_url .. "/" .. installed_module.name .. ".git"
local distinfo_file = sys.make_path(deploy_dir, cfg.distinfos_dir, installed_module.name .. "-" .. installed_module.version, "dist.info")
-- create temporary directory (delete previous if already exists)
if sys.exists(tmp_dir) then sys.delete(tmp_dir) end
local ok, err = sys.make_dir(tmp_dir)
if not ok then return nil, err end
-- copy the module files for all enabled components
for _, component in ipairs(cfg.components) do
if installed_module.files[component] then
for _, file in ipairs(installed_module.files[component]) do
local file_path = sys.make_path(deploy_dir, file)
local dest_dir = sys.parent_dir(sys.make_path(tmp_dir, file))
if sys.is_file(file_path) then
sys.make_dir(dest_dir)
sys.copy(file_path, dest_dir)
end
end
end
end
-- add module's dist.info file
sys.copy(distinfo_file, tmp_dir)
-- create git repo
ok, err = git.init(tmp_dir)
if not ok then return nil, "Error initializing empty git repository in '" .. tmp_dir .. "': " .. err end
-- add all files
ok, err = git.add_all(tmp_dir)
if not ok then return nil, "Error adding all files to the git index in '" .. tmp_dir .. "': " .. err end
-- create commit
ok, err = git.commit("[luadist-git] add " .. full_name .. " [ci skip]", tmp_dir)
if not ok then return nil, "Error commiting changes in '" .. tmp_dir .. "': " .. err end
-- rename branch
ok, err = git.rename_branch("master", branch_name, tmp_dir)
if not ok then return nil, "Error renaming branch 'master' to '" .. branch_name .. "' in '" .. tmp_dir .. "': " .. err end
-- create tag
ok, err = git.create_tag(tmp_dir, tag_name)
if not ok then return nil, "Error creating tag '" .. tag_name .. "' in '" .. tmp_dir .. "': " .. err end
print("Uploading " .. full_name .. " to " .. dest_git_url .. "...")
-- push to the repository
ok, err = git.push_ref(tmp_dir, branch_name, dest_git_url, true)
if not ok then return nil, "Error when pushing branch '" .. branch_name .. "' and tag '" .. tag_name .. "' to '" .. dest_git_url .. "': " .. err end
-- delete temporary directory (if not in debug mode)
if not cfg.debug then sys.delete(tmp_dir) end
end
return #modules_to_upload
end
-- Returns table with information about module's dependencies, using the cache.
function dependency_info(module, deploy_dir)
cache_file = cache_file or sys.abs_path(sys.make_path(cfg.root_dir, cfg.dep_cache_file))
assert(type(module) == "string", "dist.dependency_info: Argument 'module' is not a string.")
assert(type(deploy_dir) == "string", "dist.dependency_info: Argument 'deploy_dir' is not a string.")
-- get manifest
local manifest, err = mf.get_manifest()
if not manifest then return nil, "Error getting manifest: " .. err end
-- get dependency manifest
-- TODO: Is it good that dep_manifest is deploy_dir-specific?
-- Probably it'd be better not to be specific, but then there're
-- problems with 'provides'. E.g. What to do if there's a module
-- installed, that is provided by two different modules in two deploy_dirs?
local dep_manifest_file = sys.abs_path(sys.make_path(deploy_dir, cfg.dep_cache_file))
local dep_manifest, status = {}
if sys.exists(dep_manifest_file) and cfg.cache and not utils.cache_timeout_expired(cfg.cache_timeout, dep_manifest_file) then
status, dep_manifest = mf.load_manifest(dep_manifest_file)
if not dep_manifest then return nil, status end
end
-- force getting the dependency information
local installed = {}
-- resolve dependencies
local dependencies, dep_manifest_or_err = depends.get_depends(module, installed, manifest, dep_manifest, deploy_dir, false, true and not cfg.debug)
if not dependencies then return nil, dep_manifest_or_err end
-- save updated dependency manifest
local ok, err = sys.make_dir(sys.parent_dir(dep_manifest_file))
if not ok then return nil, err end
ok, err = mf.save_manifest(dep_manifest_or_err, dep_manifest_file)
if not ok then return nil, err end
-- collect just relevant dependencies from dependency manifest
local relevant_deps = {}
for _, dep in pairs(dependencies) do
local name_ver = dep.name .. "-" .. (dep.was_scm_version and "scm" or dep.version)
if dep_manifest_or_err[name_ver] then
table.insert(relevant_deps, dep_manifest_or_err[name_ver])
else
return nil, "Error: dependency information for '" .. name_ver .. "' not found in dependency manifest."
end
end
return relevant_deps
end

View File

@ -1,64 +0,0 @@
-- Simple logger for LuaDist.
module ("dist.logger", package.seeall)
local cfg = require "dist.config"
local sys = require "dist.sys"
-- Open 'log_file' and return a log, or nil and error msg on error.
local function get_log(log_file)
log_file = log_file or cfg.log_file
assert(type(log_file) == "string", "log.get_log: Argument 'log_file' is not a string.")
log_file = sys.abs_path(log_file)
sys.make_dir(sys.parent_dir(log_file))
local log, err = io.open(log_file, "a")
if not log then
return nil, "Error: can't open a logfile '" .. log_file .. "': " .. err
else
return log
end
end
-- Set the default log.
local log_file = get_log(cfg.log_file)
-- Log levels used.
local log_levels = {
DEBUG = 0, -- Fine-grained informational events that are most useful to debug an application.
INFO = 1, -- Informational messages that highlight the progress of the application at coarse-grained level.
WARN = 2, -- Potentially harmful situations.
ERROR = 3, -- Error events that might still allow the application to continue running.
FATAL = 4, -- Very severe error events that would presumably lead the application to abort.
}
-- Write 'message' with 'level' to 'log'.
local function write(level, ...)
assert(type(level) == "string", "log.write: Argument 'level' is not a string.")
assert(#arg > 0, "log.write: No message arguments provided.")
assert(type(log_levels[level]) == "number", "log.write: Unknown log level used: '" .. level .. "'.")
level = level:upper()
local message = table.concat(arg, " ")
-- Check if writing for this log level is enabled.
if cfg.write_log_level and log_levels[level] >= log_levels[cfg.write_log_level] then
log_file:write(os.date("%Y-%m-%d %H:%M:%S") .. " [" .. level .. "]\t" .. message .. "\n")
log_file:flush()
end
-- Check if printing for this log level is enabled.
if cfg.print_log_level and log_levels[level] >= log_levels[cfg.print_log_level] then
print(message)
end
end
-- Functions with defined log levels for simple use.
function debug(...) return write("DEBUG", ...) end
function info(...) return write("INFO", ...) end
function warn(...) return write("WARN", ...) end
function error(...) return write("ERROR", ...) end
function fatal(...) return write("FATAL", ...) end
-- Function with explicitly specified log level.
function log(level, ...) return write(level, ...) end

View File

@ -1,248 +0,0 @@
-- Working with manifest and dist.info files
module ("dist.manifest", package.seeall)
local cfg = require "dist.config"
local git = require "dist.git"
local sys = require "dist.sys"
local utils = require "dist.utils"
-- Return the manifest table from 'manifest_file'. If the manifest is in cache,
-- then the cached version is used. You can set the cache timeout value in
-- 'config.cache_timeout' variable.
-- If optional 'force_no_cache' parameter is true, then the cache is not used.
function get_manifest(manifest_file, force_no_cache)
manifest_file = manifest_file or sys.make_path(cfg.root_dir, cfg.manifest_file)
force_no_cache = force_no_cache or false
assert(type(manifest_file) == "string", "manifest.get_manifest: Argument 'manifest_file' is not a string.")
assert(type(force_no_cache) == "boolean", "manifest.get_manifest: Argument 'force_no_cache' is not a boolean.")
manifest_file = sys.abs_path(manifest_file)
-- download new manifest to the cache if not present or cache not used or cache expired
if not sys.exists(manifest_file) or force_no_cache or not cfg.cache or utils.cache_timeout_expired(cfg.cache_timeout, manifest_file) then
local manifest_dest = sys.parent_dir(manifest_file) or sys.current_dir()
local ok, err = download_manifest(manifest_dest, cfg.repos)
if not ok then return nil, "Error when downloading manifest: " .. err end
end
-- load manifest from cache
local status, ret = load_manifest(manifest_file)
if not status then return nil, "Error when loading manifest: " .. ret end
return ret
end
-- Download manifest from the table of git 'repository_urls' to 'dest_dir' and return true on success
-- and nil and error message on error.
function download_manifest(dest_dir, repository_urls)
dest_dir = dest_dir or sys.make_path(cfg.root_dir, cfg.cache_dir)
repository_urls = repository_urls or cfg.repos
if type(repository_urls) == "string" then repository_urls = {repository_urls} end
assert(type(dest_dir) == "string", "manifest.download_manifest: Argument 'dest_dir' is not a string.")
assert(type(repository_urls) == "table", "manifest.download_manifest: Argument 'repository_urls' is not a table or string.")
dest_dir = sys.abs_path(dest_dir)
-- define used files and directories
local manifest_filename = sys.extract_name(cfg.manifest_file)
local manifest_file = sys.make_path(dest_dir, manifest_filename)
local temp_dir = sys.make_path(cfg.root_dir, cfg.temp_dir)
-- ensure that destination directory exists
local ok, err = sys.make_dir(dest_dir)
if not ok then return nil, err end
-- retrieve manifests from repositories and collect them into one manifest table
local manifest = {}
if #repository_urls == 0 then return nil, "No repository url specified." end
print("Downloading repository information...")
for k, repo in pairs(repository_urls) do
local clone_dir = sys.make_path(temp_dir, "repository_" .. tostring(k))
-- clone the repo and add its '.gitmodules' file to the manifest table
ok, err = git.create_repo(clone_dir)
local sha
if ok then sha, err = git.fetch_branch(clone_dir, repo, "master") end
if sha then ok, err = git.checkout_sha(sha, clone_dir) end
if not (ok and sha) then
if not cfg.debug then sys.delete(clone_dir) end
return nil, "Error when downloading the manifest from repository with url: '" .. repo .. "': " .. err
else
for _, pkg in pairs(load_gitmodules(sys.make_path(clone_dir, ".gitmodules"))) do
table.insert(manifest, pkg)
end
end
if not cfg.debug then sys.delete(clone_dir) end
end
-- save the new manifest table to the file
ok, err = save_manifest(manifest, manifest_file)
if not ok then return nil, err end
return true
end
-- A secure loadfile function
-- If file code chunk has upvalues, the first upvalue is set to the given
-- environement, if that parameter is given, or to the value of the global environment.
local function secure_loadfile(file, env)
assert(type(file) == "string", "secure_loadfile: Argument 'file' is not a string.")
-- use the given (or create a new) restricted environment
local env = env or {}
-- load the file and run in a protected call with the restricted env
-- setfenv is deprecated in lua 5.2 in favor of giving env in arguments
-- the additional loadfile arguments are simply ignored for previous lua versions
local f, err = loadfile(file, 'bt', env)
if f then
if setfenv ~= nil then
setfenv(f, env)
end
return pcall(f)
else
return nil, err
end
end
-- Load and return manifest table from the manifest file.
-- If manifest file not present, return nil.
function load_manifest(manifest_file)
manifest_file = manifest_file or sys.make_path(cfg.root_dir, cfg.manifest_file)
return secure_loadfile(sys.abs_path(manifest_file))
end
-- Load '.gitmodules' file and returns manifest table.
-- If the file is not present, return nil.
function load_gitmodules(gitmodules_file)
gitmodules_file = gitmodules_file or sys.make_path(cfg.root_dir, cfg.manifest_file)
assert(type(gitmodules_file) == "string", "manifest.load_gitmodules: Argument 'gitmodules_file' is not a string.")
gitmodules_file = sys.abs_path(gitmodules_file)
if sys.exists(gitmodules_file) then
-- load the .gitmodules file
local file, err = io.open(gitmodules_file, "r")
if not file then return nil, "Error when opening the .gitmodules file '" .. gitmodules_file .. "':" .. err end
local mf_text = file:read("*a")
file:close()
if not mf_text then return nil, "Error when reading the .gitmodules file '" .. gitmodules_file .. "':" .. err end
manifest = {}
for url in mf_text:gmatch("git://%S+/%S+") do
pkg = {name = url:match("git://%S+/(%S+)%.git") or url:match("git://%S+/(%S+)"), version = "scm", path = url}
table.insert(manifest, pkg)
end
return manifest
else
return nil, "Error when loading the .gitmodules: file '" .. gitmodules_file .. "' doesn't exist."
end
end
-- Save manifest table to the 'file'
function save_manifest(manifest_table, file)
assert(type(manifest_table) == "table", "manifest.save_distinfo: Argument 'manifest_table' is not a table.")
assert(type(file) == "string", "manifest.save_distinfo: Argument 'file' is not a string.")
file = sys.abs_path(file)
-- Print table 'tbl' to io stream 'file'.
local function print_table(file, tbl, in_nested_table)
for k, v in pairs(tbl) do
-- print key
if in_nested_table then file:write("\t\t") end
if type(k) ~= "number" then
file:write("['" .. k .. "']" .. " = ")
end
-- print value
if type(v) == "table" then
file:write("{\n")
print_table(file, v, true)
if in_nested_table then file:write("\t") end
file:write("\t}")
else
if in_nested_table then file:write("\t") end
if type(v) == "string" then
file:write('[[' .. v .. ']]')
else
file:write(tostring(v))
end
end
file:write(",\n")
end
end
local manifest_file = io.open(file, "w")
if not manifest_file then return nil, "Error when saving manifest: cannot open the file '" .. file .. "'." end
manifest_file:write('return {\n')
print_table(manifest_file, manifest_table)
manifest_file:write('},\ntrue')
manifest_file:close()
return true
end
-- Load and return package info table from the distinfo_file file.
-- If file not present, return nil.
function load_distinfo(distinfo_file)
assert(type(distinfo_file) == "string", "manifest.load_distinfo: Argument 'distinfo_file' is not a string.")
distinfo_file = sys.abs_path(distinfo_file)
-- load the distinfo file
local distinfo_env = {}
local status, ret = secure_loadfile(distinfo_file, distinfo_env)
if not status then return nil, "Error when loading package info: " .. ret end
return distinfo_env
end
-- Save distinfo table to the 'file'
function save_distinfo(distinfo_table, file)
assert(type(distinfo_table) == "table", "manifest.save_distinfo: Argument 'distinfo_table' is not a table.")
assert(type(file) == "string", "manifest.save_distinfo: Argument 'file' is not a string.")
file = sys.abs_path(file)
-- Print table 'tbl' to io stream 'file'.
local function print_table(file, tbl, in_nested_table)
for k, v in pairs(tbl) do
-- print key
if type(k) ~= "number" then
file:write(k .. " = ")
end
-- print value
if type(v) == "table" then
file:write("{\n")
print_table(file, v, true)
file:write("}\n")
elseif type(v) == "string" then
if in_nested_table then
file:write('[[' .. v .. ']]')
else
file:write('"' .. v .. '"')
end
else
file:write(v)
end
if in_nested_table then
file:write(",")
end
file:write("\n")
end
end
local distinfo_file = io.open(file, "w")
if not distinfo_file then return nil, "Error when saving dist-info table: cannot open the file '" .. file .. "'." end
print_table(distinfo_file, distinfo_table)
distinfo_file:close()
return true
end

View File

@ -1,596 +0,0 @@
-- Package functions
module ("dist.package", package.seeall)
local cfg = require "dist.config"
local git = require "dist.git"
local sys = require "dist.sys"
local mf = require "dist.manifest"
local utils = require "dist.utils"
local depends = require "dist.depends"
-- Return whether the package in given 'pkg_dir' is of a source type.
function is_source_type(pkg_dir)
assert(type(pkg_dir) == "string", "package.is_source_type: Argument 'pkg_dir' is not a string.")
pkg_dir = sys.abs_path(pkg_dir)
return utils.to_boolean(sys.exists(sys.make_path(pkg_dir, "CMakeLists.txt")))
end
-- Ensure proper arch and type for the given source 'dist_info' table and return it.
-- WARNING: this function should be used only for 'dist_info' tables of modules that are of a source type!
function ensure_source_arch_and_type(dist_info)
assert(type(dist_info) == "table", "package.ensure_source_arch_and_type: Argument 'dist_info' is not a table.")
dist_info.arch = dist_info.arch or "Universal"
dist_info.type = dist_info.type or "source"
return dist_info
end
-- Remove package from 'pkg_distinfo_dir' of 'deploy_dir'.
function remove_pkg(pkg_distinfo_dir, deploy_dir)
deploy_dir = deploy_dir or cfg.root_dir
assert(type(pkg_distinfo_dir) == "string", "package.remove_pkg: Argument 'pkg_distinfo_dir' is not a string.")
assert(type(deploy_dir) == "string", "package.remove_pkg: Argument 'deploy_dir' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
local abs_pkg_distinfo_dir = sys.make_path(deploy_dir, pkg_distinfo_dir)
-- check for 'dist.info'
local info, err = mf.load_distinfo(sys.make_path(abs_pkg_distinfo_dir, "dist.info"))
if not info then return nil, "Error removing package from '" .. pkg_distinfo_dir .. "' - it doesn't contain valid 'dist.info' file." end
if not info.files then return nil, "File '" .. sys.make_path(pkg_distinfo_dir, "dist.info") .."' doesn't contain list of installed files." end
-- remove files installed as components of this package
for _, component in ipairs(cfg.components) do
if info.files[component] then
for i = #info.files[component], 1, -1 do
local f = info.files[component][i]
f = sys.make_path(deploy_dir,f)
if sys.is_file(f) then
sys.delete(f)
elseif sys.is_dir(f) then
local dir_files, err = sys.get_file_list(f)
if not dir_files then return nil, "Error removing package in '" .. abs_pkg_distinfo_dir .. "': " .. err end
if #dir_files == 0 then sys.delete(f) end
end
-- delete also all parent directories if empty
local parents = sys.parents_up_to(f, deploy_dir)
for _, parent in ipairs(parents) do
if sys.is_dir(parent) then
local dir_files, err = sys.get_file_list(parent)
if not dir_files then return nil, "Error removing package in '" .. abs_pkg_distinfo_dir .. "': " .. err end
if #dir_files == 0 then
sys.delete(parent)
end
end
end
end
end
end
-- remove removed components also from 'dist.info'
for _, component in ipairs(cfg.components) do
info.files[component] = nil
end
-- delete the package information from deploy_dir
local ok = sys.delete(abs_pkg_distinfo_dir)
if not ok then return nil, "Error removing package in '" .. abs_pkg_distinfo_dir .. "'." end
-- if the package was not completely removed (e.g. some components remain),
-- save the new version of its 'dist.info'
local comp_num = 0
for _, _ in pairs(info.files) do comp_num = comp_num + 1 end
if comp_num ~= 0 then
sys.make_dir(abs_pkg_distinfo_dir)
local ok, err = mf.save_distinfo(info, sys.make_path(abs_pkg_distinfo_dir, "dist.info"))
if not ok then return nil, "Error resaving the 'dist.info': " .. err end
end
return ok
end
-- Install package from 'pkg_dir' to 'deploy_dir', using optional CMake 'variables'.
-- Optional 'preserve_pkg_dir' argument specified whether to preserve the 'pkg_dir'.
function install_pkg(pkg_dir, deploy_dir, variables, preserve_pkg_dir)
deploy_dir = deploy_dir or cfg.root_dir
variables = variables or {}
preserve_pkg_dir = preserve_pkg_dir or false
assert(type(pkg_dir) == "string", "package.install_pkg: Argument 'pkg_dir' is not a string.")
assert(type(deploy_dir) == "string", "package.install_pkg: Argument 'deploy_dir' is not a string.")
assert(type(variables) == "table", "package.install_pkg: Argument 'variables' is not a table.")
assert(type(preserve_pkg_dir) == "boolean", "package.install_pkg: Argument 'preserve_pkg_dir' is not a boolean.")
pkg_dir = sys.abs_path(pkg_dir)
deploy_dir = sys.abs_path(deploy_dir)
-- check for dist.info
local info, err = mf.load_distinfo(sys.make_path(pkg_dir, "dist.info"))
if not info then return nil, "Error installing: the directory '" .. pkg_dir .. "' doesn't exist or doesn't contain valid 'dist.info' file." end
-- check if the package is source
if is_source_type(pkg_dir) then info = ensure_source_arch_and_type(info) end
-- check package's architecture
if not (info.arch == "Universal" or info.arch == cfg.arch) then
return nil, "Error installing '" .. info.name .. "-" .. info.version .. "': architecture '" .. info.arch .. "' is not suitable for this machine."
end
-- check package's type
if not (info.type == "all" or info.type == "source" or info.type == cfg.type) then
return nil, "Error installing '" .. info.name .. "-" .. info.version .. "': architecture type '" .. info.type .. "' is not suitable for this machine."
end
local ok, err
-- if package is of binary type, just deploy it
if info.type ~= "source" then
ok, err = deploy_binary_pkg(pkg_dir, deploy_dir)
-- else build and then deploy
else
-- check if we have cmake
ok = utils.system_dependency_available("cmake", "cmake --version")
if not ok then return nil, "Error when installing: Command 'cmake' not available on the system." end
-- set cmake variables
local cmake_variables = {}
-- set variables from config file
for k, v in pairs(cfg.variables) do
cmake_variables[k] = v
end
-- set variables specified as argument
for k, v in pairs(variables) do
cmake_variables[k] = v
end
cmake_variables.CMAKE_INCLUDE_PATH = table.concat({cmake_variables.CMAKE_INCLUDE_PATH or "", sys.make_path(deploy_dir, "include")}, ";")
cmake_variables.CMAKE_LIBRARY_PATH = table.concat({cmake_variables.CMAKE_LIBRARY_PATH or "", sys.make_path(deploy_dir, "lib"), sys.make_path(deploy_dir, "bin")}, ";")
cmake_variables.CMAKE_PROGRAM_PATH = table.concat({cmake_variables.CMAKE_PROGRAM_PATH or "", sys.make_path(deploy_dir, "bin")}, ";")
-- build the package and deploy it
ok, err = build_pkg(pkg_dir, deploy_dir, cmake_variables)
if not ok then return nil, err end
end
-- delete directory of fetched package
if not (cfg.debug or preserve_pkg_dir) then sys.delete(pkg_dir) end
return ok, err
end
-- Build and deploy package from 'src_dir' to 'deploy_dir' using 'variables'.
-- Return directory to which the package was built or nil on error.
-- 'variables' is table of optional CMake variables.
function build_pkg(src_dir, deploy_dir, variables)
deploy_dir = deploy_dir or cfg.root_dir
variables = variables or {}
assert(type(src_dir) == "string", "package.build_pkg: Argument 'src_dir' is not a string.")
assert(type(deploy_dir) == "string", "package.build_pkg: Argument 'deploy_dir' is not a string.")
assert(type(variables) == "table", "package.build_pkg: Argument 'variables' is not a table.")
src_dir = sys.abs_path(src_dir)
deploy_dir = sys.abs_path(deploy_dir)
-- check for dist.info
local info, err = mf.load_distinfo(sys.make_path(src_dir, "dist.info"))
if not info then return nil, "Error building package from '" .. src_dir .. "': it doesn't contain valid 'dist.info' file." end
local pkg_name = info.name .. "-" .. info.version
-- set machine information
info.arch = cfg.arch
info.type = cfg.type
-- create CMake build dir
local cmake_build_dir = sys.abs_path(sys.make_path(deploy_dir, cfg.temp_dir, pkg_name .. "-CMake-build"))
sys.make_dir(cmake_build_dir)
-- create cmake cache
variables["CMAKE_INSTALL_PREFIX"] = deploy_dir
local cache_file = io.open(sys.make_path(cmake_build_dir, "cache.cmake"), "w")
if not cache_file then return nil, "Error creating CMake cache file in '" .. cmake_build_dir .. "'" end
-- Fill in cache variables
for k,v in pairs(variables) do
cache_file:write("SET(" .. k .. " " .. sys.quote(v):gsub("\\+", "/") .. " CACHE STRING \"\" FORCE)\n")
end
-- If user cache file is provided then append it
if cfg.cache_file ~= "" then
local user_cache = io.open(sys.abs_path(cfg.cache_file), "r")
if user_cache then
cache_file:write(user_cache:read("*all").."\n")
user_cache:close()
end
end
cache_file:close()
src_dir = sys.abs_path(src_dir)
print("Building " .. sys.extract_name(src_dir) .. "...")
-- set cmake cache command
local cache_command = cfg.cache_command
if cfg.debug then cache_command = cache_command .. " " .. cfg.cache_debug_options end
-- set cmake build command
local build_command = cfg.build_command
if cfg.debug then build_command = build_command .. " " .. cfg.build_debug_options end
-- set the cmake cache
local ok = sys.exec("cd " .. sys.quote(cmake_build_dir) .. " && " .. cache_command .. " " .. sys.quote(src_dir))
if not ok then return nil, "Error preloading the CMake cache script '" .. sys.make_path(cmake_build_dir, "cache.cmake") .. "'" end
-- build with cmake
ok = sys.exec("cd " .. sys.quote(cmake_build_dir) .. " && " .. build_command)
if not ok then return nil, "Error building with CMake in directory '" .. cmake_build_dir .. "'" end
-- if this is only simulation, exit sucessfully, skipping the next actions
if cfg.simulate then
return true, "Simulated build and deployment of package '" .. pkg_name .. "' sucessfull."
end
-- table to collect files installed in the components
info.files = {}
-- install the components
for _, component in ipairs(cfg.components) do
local strip_option = ""
if not cfg.debug and component ~= "Library" then strip_option = cfg.strip_option end
local ok = sys.exec("cd " .. sys.quote(cmake_build_dir) .. " && " .. cfg.cmake .. " " .. strip_option .. " " ..cfg.install_component_command:gsub("#COMPONENT#", component))
if not ok then return nil, "Error when installing the component '" .. component .. "' with CMake in directory '" .. cmake_build_dir .. "'" end
local install_mf = sys.make_path(cmake_build_dir, "install_manifest_" .. component .. ".txt")
local mf, err
local component_files = {}
-- collect files installed in this component
if sys.exists(install_mf) then
mf, err = io.open(install_mf, "r")
if not mf then return nil, "Error when opening the CMake installation manifest '" .. install_mf .. "': " .. err end
for line in mf:lines() do
line = sys.check_separators(line)
local file = line:gsub(utils.escape_magic(deploy_dir .. sys.path_separator()), "")
table.insert(component_files, file)
end
mf:close()
-- add list of component files to the 'dist.info'
if #component_files > 0 then info.files[component] = component_files end
end
end
-- if bookmark == 0 then return nil, "Package did not install any files!" end
-- test with ctest
if cfg.test then
print("Testing " .. sys.extract_name(src_dir) .. " ...")
ok = sys.exec("cd " .. sys.quote(deploy_dir) .. " && " .. cfg.test_command)
if not ok then return nil, "Error when testing the module '" .. pkg_name .. "' with CTest." end
end
-- save modified 'dist.info' file
local pkg_distinfo_dir = sys.make_path(deploy_dir, cfg.distinfos_dir, pkg_name)
sys.make_dir(pkg_distinfo_dir)
ok, err = mf.save_distinfo(info, sys.make_path(pkg_distinfo_dir, "dist.info"))
if not ok then return nil, err end
-- clean up
if not cfg.debug then sys.delete(cmake_build_dir) end
return true, "Package '" .. pkg_name .. "' successfully builded and deployed to '" .. deploy_dir .. "'."
end
-- Deploy binary package from 'pkg_dir' to 'deploy_dir' by copying.
function deploy_binary_pkg(pkg_dir, deploy_dir)
deploy_dir = deploy_dir or cfg.root_dir
assert(type(pkg_dir) == "string", "package.deploy_binary_pkg: Argument 'pkg_dir' is not a string.")
assert(type(deploy_dir) == "string", "package.deploy_binary_pkg: Argument 'deploy_dir' is not a string.")
pkg_dir = sys.abs_path(pkg_dir)
deploy_dir = sys.abs_path(deploy_dir)
-- check for dist.info
local info, err = mf.load_distinfo(sys.make_path(pkg_dir, "dist.info"))
if not info then return nil, "Error deploying package from '" .. pkg_dir .. "': it doesn't contain valid 'dist.info' file." end
local pkg_name = info.name .. "-" .. info.version
-- if this is only simulation, exit sucessfully, skipping the next actions
if cfg.simulate then
return true, "Simulated deployment of package '" .. pkg_name .. "' sucessfull."
end
-- copy all components of the module to the deploy_dir
for _, component in ipairs(cfg.components) do
if info.files[component] then
for _, file in ipairs(info.files[component]) do
local dest_dir = sys.make_path(deploy_dir, sys.parent_dir(file))
local ok, err = sys.make_dir(dest_dir)
if not ok then return nil, "Error when deploying package '" .. pkg_name .. "': cannot create directory '" .. dest_dir .. "': " .. err end
ok, err = sys.copy(sys.make_path(pkg_dir, file), dest_dir)
if not ok then return nil, "Error when deploying package '" .. pkg_name .. "': cannot copy file '" .. file .. "' to the directory '" .. dest_dir .. "': " .. err end
end
end
end
-- copy dist.info to register the module as installed
local pkg_distinfo_dir = sys.make_path(deploy_dir, cfg.distinfos_dir, pkg_name)
sys.make_dir(pkg_distinfo_dir)
ok, err = mf.save_distinfo(info, sys.make_path(pkg_distinfo_dir, "dist.info"))
if not ok then return nil, err end
return true, "Package '" .. pkg_name .. "' successfully deployed to '" .. deploy_dir .. "'."
end
-- Fetch package (table 'pkg') to download_dir. Return the original 'pkg' table
-- with 'pkg.download_dir' containing path to the directory of the
-- downloaded package.
--
-- When optional 'suppress_printing' parameter is set to true, then messages
-- for the user won't be printed during run of this function.
--
-- If the 'pkg' already contains the information about download directory (pkg.download_dir),
-- we assume the package was already downloaded there and won't download it again.
function fetch_pkg(pkg, download_dir, suppress_printing)
download_dir = download_dir or sys.current_dir()
suppress_printing = suppress_printing or false
assert(type(pkg) == "table", "package.fetch_pkg: Argument 'pkg' is not a table.")
assert(type(download_dir) == "string", "package.fetch_pkg: Argument 'download_dir' is not a string.")
assert(type(suppress_printing) == "boolean", "package.fetch_pkg: Argument 'suppress_printing' is not a boolean.")
assert(type(pkg.name) == "string", "package.fetch_pkg: Argument 'pkg.name' is not a string.")
assert(type(pkg.version) == "string", "package.fetch_pkg: Argument 'pkg.version' is not a string.")
-- if the package is already downloaded don't download it again
if pkg.download_dir then return pkg end
assert(type(pkg.path) == "string", "package.fetch_pkg: Argument 'pkg.path' is not a string.")
download_dir = sys.abs_path(download_dir)
local pkg_full_name = pkg.name .. "-" .. pkg.version
local repo_url = pkg.path
local clone_dir = sys.abs_path(sys.make_path(download_dir, pkg_full_name))
pkg.download_dir = clone_dir
-- check if download_dir already exists, assuming the package was already downloaded
if sys.exists(sys.make_path(clone_dir, "dist.info")) then
if cfg.cache and not utils.cache_timeout_expired(cfg.cache_timeout, clone_dir) then
if not suppress_printing then print("'" .. pkg_full_name .. "' already in cache, skipping downloading (use '-cache=false' to force download).") end
return pkg
else
sys.delete(sys.make_path(clone_dir))
end
end
local bin_tag = pkg.version .. "-" .. cfg.arch .. "-" .. cfg.type
local use_binary = false
if cfg.binary then
-- check if binary version of the module for this arch & type available
local avail_tags, err = git.get_remote_tags(repo_url)
if not avail_tags then return nil, err end
if utils.contains(avail_tags, bin_tag) then
use_binary = true
end
end
-- init the git repository
local ok, err = git.create_repo(clone_dir)
if not ok then return nil, err end
-- Fetch the desired ref (from the pkg's remote repo) and checkout into it.
if use_binary then
if not suppress_printing then print("Getting " .. pkg_full_name .. " (binary)...") end
-- We fetch the binary tag.
local sha
if ok then sha, err = git.fetch_tag(clone_dir, repo_url, bin_tag) end
if sha then ok, err = git.checkout_sha(sha, clone_dir) end
elseif cfg.source then
if not suppress_printing then print("Getting " .. pkg_full_name .. " (source)...") end
-- If we want the 'scm' version, we fetch the 'master' branch, otherwise
-- we fetch the tag, matching the desired package version.
if ok and pkg.version ~= "scm" then
local sha
sha, err = git.fetch_tag(clone_dir, repo_url, pkg.version)
if sha then ok, err = git.checkout_sha(sha, clone_dir) end
elseif ok then
local sha
sha, err = git.fetch_branch(clone_dir, repo_url, "master")
if sha then ok, err = git.checkout_sha(sha, clone_dir) end
end
else
ok = false
if cfg.binary then
err = "Binary version of module not available and using source modules disabled."
else
err = "Using both binary and source modules disabled."
end
end
if not ok then
-- clean up
if not cfg.debug then sys.delete(clone_dir) end
return nil, "Error fetching package '" .. pkg_full_name .. "' from '" .. pkg.path .. "' to '" .. download_dir .. "': " .. err
end
-- delete '.git' directory
if not cfg.debug then sys.delete(sys.make_path(clone_dir, ".git")) end
return pkg
end
-- Return table with information about available versions of 'package'.
--
-- When optional 'suppress_printing' parameter is set to true, then messages
-- for the user won't be printed during run of this function.
function retrieve_versions(package, manifest, suppress_printing)
suppress_printing = suppress_printing or false
assert(type(package) == "string", "package.retrieve_versions: Argument 'string' is not a string.")
assert(type(manifest) == "table", "package.retrieve_versions: Argument 'manifest' is not a table.")
assert(type(suppress_printing) == "boolean", "package.retrieve_versions: Argument 'suppress_printing' is not a boolean.")
-- get package table
local pkg_name = depends.split_name_constraint(package)
local tmp_packages = depends.find_packages(pkg_name, manifest)
if #tmp_packages == 0 then
return nil, "No suitable candidate for package '" .. package .. "' found."
else
package = tmp_packages[1]
end
-- if the package's already downloaded, we assume it's desired to install the downloaded version
if package.download_dir then
local pkg_type = "binary"
if is_source_type(package.download_dir) then pkg_type = "source" end
if not suppress_printing then print("Using " .. package.name .. "-" .. package.version .. " (" .. pkg_type .. ") provided by " .. package.download_dir) end
return {package}
end
if not suppress_printing then print("Finding out available versions of " .. package.name .. "...") end
-- get available versions
local tags, err = git.get_remote_tags(package.path)
if not tags then return nil, "Error when retrieving versions of package '" .. package.name .. "': " .. err end
-- filter out tags of binary packages
local versions = utils.filter(tags, function (tag) return tag:match("^[^%-]+%-?[^%-]*$") and true end)
packages = {}
-- create package information
for _, version in pairs(versions) do
pkg = {}
pkg.name = package.name
pkg.version = version
pkg.path = package.path
table.insert(packages, pkg)
end
return packages
end
-- Return table with information from package's dist.info and path to downloaded
-- package. Optional argument 'deploy_dir' is used just as a temporary
-- place to place the downloaded packages into.
--
-- When optional 'suppress_printing' parameter is set to true, then messages
-- for the user won't be printed during the execution of this function.
function retrieve_pkg_info(package, deploy_dir, suppress_printing)
deploy_dir = deploy_dir or cfg.root_dir
assert(type(package) == "table", "package.retrieve_pkg_info: Argument 'package' is not a table.")
assert(type(deploy_dir) == "string", "package.retrieve_pkg_info: Argument 'deploy_dir' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
local tmp_dir = sys.abs_path(sys.make_path(deploy_dir, cfg.temp_dir))
-- download the package
local fetched_pkg, err = fetch_pkg(package, tmp_dir, suppress_printing)
if not fetched_pkg then return nil, "Error when retrieving the info about '" .. package.name .. "': " .. err end
-- load information from 'dist.info'
local info, err = mf.load_distinfo(sys.make_path(fetched_pkg.download_dir, "dist.info"))
if not info then return nil, err end
-- add other attributes
if package.path then info.path = package.path end
if package.was_scm_version then info.was_scm_version = package.was_scm_version end
-- set default arch/type if not explicitly stated and package is of source type
if is_source_type(fetched_pkg.download_dir) then
info = ensure_source_arch_and_type(info)
elseif not (info.arch and info.type) then
return nil, fetched_pkg.download_dir .. ": binary package missing arch or type in 'dist.info'."
end
return info, fetched_pkg.download_dir
end
-- Return manifest, augmented with info about all available versions
-- of package 'pkg'. Optional argument 'deploy_dir' is used just as a temporary
-- place to place the downloaded packages into.
-- Optional argument 'installed' is manifest of all installed packages. When
-- specified, info from installed packages won't be downloaded from repo,
-- but the dist.info from installed package will be used.
function get_versions_info(pkg, manifest, deploy_dir, installed)
deploy_dir = deploy_dir or cfg.root_dir
assert(type(pkg) == "string", "package.get_versions_info: Argument 'pkg' is not a string.")
assert(type(manifest) == "table", "package.get_versions_info: Argument 'manifest' is not a table.")
assert(type(deploy_dir) == "string", "package.get_versions_info: Argument 'deploy_dir' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
-- find all available versions of package
local versions, err = retrieve_versions(pkg, manifest)
if not versions then return nil, err end
-- collect info about all retrieved versions
local infos = {}
for _, version in pairs(versions) do
local info, path_or_err
local installed_version = {}
-- find out whether this 'version' is installed so we can use it's dist.info
if type(installed) == "table" then installed_version = depends.find_packages(version.name .. "-" .. version.version, installed) end
-- get info
if #installed_version > 0 then
print("Using dist.info from installed " .. version.name .. "-" .. version.version)
info = installed_version[1]
info.path = version.path
info.from_installed = true -- flag that dist.info of installed package was used
else
info, path_or_err = retrieve_pkg_info(version, deploy_dir)
if not info then return nil, path_or_err end
sys.delete(path_or_err)
end
table.insert(infos, info)
end
-- found and add an implicit 'scm' version
local pkg_name = depends.split_name_constraint(pkg)
local found = depends.find_packages(pkg_name, manifest)
if #found == 0 then return nil, "No suitable candidate for package '" .. pkg .. "' found." end
local scm_info, path_or_err = retrieve_pkg_info({name = pkg_name, version = "scm", path = found[1].path})
if not scm_info then return nil, path_or_err end
sys.delete(path_or_err)
scm_info.version = "scm"
table.insert(infos, scm_info)
local tmp_manifest = utils.deepcopy(manifest)
-- add collected info to the temp. manifest, replacing existing tables
for _, info in pairs(infos) do
local already_in_manifest = false
-- find if this version is already in manifest
for idx, pkg in ipairs(tmp_manifest) do
-- if yes, replace it
if pkg.name == info.name and pkg.version == info.version then
tmp_manifest[idx] = info
already_in_manifest = true
break
end
end
-- if not, just normally add to the manifest
if not already_in_manifest then
table.insert(tmp_manifest, info)
end
end
return tmp_manifest
end

View File

@ -1,386 +0,0 @@
-- System functions
module ("dist.sys", package.seeall)
local cfg = require "dist.config"
local utils = require "dist.utils"
local lfs = require "lfs"
-- Return the path separator according to the platform.
function path_separator()
if cfg.arch == "Windows" then
return "\\"
else
return "/"
end
end
-- Return path with wrong separators replaced with the right ones.
function check_separators(path)
assert(type(path) == "string", "sys.check_separators: Argument 'path' is not a string.")
if cfg.arch == "Windows" then
return path:gsub("/", "\\")
else
return path
end
end
-- Return the path with the unnecessary trailing separator removed.
function remove_trailing(path)
assert(type(path) == "string", "sys.remove_trailing: Argument 'path' is not a string.")
if path:sub(-1) == path_separator() and not is_root(path) then path = path:sub(1,-2) end
return path
end
-- Return the path with the all occurences of '/.' or '\.' (representing
-- the current directory) removed.
function remove_curr_dir_dots(path)
assert(type(path) == "string", "sys.remove_curr_dir_dots: Argument 'path' is not a string.")
while path:match(path_separator() .. "%." .. path_separator()) do -- match("/%./")
path = path:gsub(path_separator() .. "%." .. path_separator(), path_separator()) -- gsub("/%./", "/")
end
return path:gsub(path_separator() .. "%.$", "") -- gsub("/%.$", "")
end
-- Return string argument quoted for a command line usage.
function quote(argument)
assert(type(argument) == "string", "sys.quote: Argument 'argument' is not a string.")
-- TODO: This seems like a not very nice hack. Why is it needed?
-- Wouldn't it be better to fix the problem where it originates?
-- replace '/' path separators for '\' on Windows
if cfg.arch == "Windows" and argument:match("^[%u%U.]?:?[/\\].*") then
argument = argument:gsub("//","\\"):gsub("/","\\")
end
-- Windows doesn't recognize paths starting with two slashes or backslashes
-- so we double every backslash except for the first one
if cfg.arch == "Windows" and argument:match("^[/\\].*") then
local prefix = argument:sub(1,1)
argument = argument:sub(2):gsub("\\", "\\\\")
argument = prefix .. argument
else
argument = argument:gsub("\\", "\\\\")
end
argument = argument:gsub('"', '\\"')
return '"' .. argument .. '"'
end
-- Run the system command (in current directory).
-- Return true on success, nil on fail and log string.
-- When optional 'force_verbose' parameter is true, then the output will be shown
-- even when not in debug or verbose mode.
function exec(command, force_verbose)
force_verbose = force_verbose or false
assert(type(command) == "string", "sys.exec: Argument 'command' is not a string.")
assert(type(force_verbose) == "boolean", "sys.exec: Argument 'force_verbose' is not a boolean.")
if not (cfg.verbose or cfg.debug or force_verbose) then
if cfg.arch == "Windows" then
command = command .. " > NUL 2>&1"
else
command = command .. " > /dev/null 2>&1"
end
end
if cfg.debug then print("Executing the command: " .. command) end
local ok, str, status = os.execute(command)
-- os.execute returned values on failure are:
-- nil or true, "exit", n or true, "signal", n for lua >= 5.2
-- status ~= 0 for lua 5.x < 5.2
if ok == nil or (str == "exit" and status ~= 0) or str == "signal" or (ok ~= 0 and ok ~= true) then
return nil, "Error when running the command: " .. command
else
return true, "Sucessfully executed the command: " .. command
end
end
-- Execute the 'command' and returns its output as a string.
function capture_output(command)
assert(type(command) == "string", "sys.exec: Argument 'command' is not a string.")
local executed, err = io.popen(command, "r")
if not executed then return nil, "Error running the command '" .. command .. "':" .. err end
local captured, err = executed:read("*a")
if not captured then return nil, "Error reading the output of command '" .. command .. "':" .. err end
executed:close()
return captured
end
-- Return whether the path is a root.
function is_root(path)
assert(type(path) == "string", "sys.is_root: Argument 'path' is not a string.")
return utils.to_boolean(path:find("^[a-zA-Z]:[/\\]$") or path:find("^[/\\]$"))
end
-- Return whether the path is absolute.
function is_abs(path)
assert(type(path) == "string", "sys.is_abs: Argument 'path' is not a string.")
return utils.to_boolean(path:find("^[a-zA-Z]:[/\\].*$") or path:find("^[/\\].*$"))
end
-- Return whether the specified file or directory exists.
function exists(path)
assert(type(path) == "string", "sys.exists: Argument 'path' is not a string.")
local attr, err = lfs.attributes(path)
return utils.to_boolean(attr), err
end
-- Return whether the 'file' exists and is a file.
function is_file(file)
assert(type(file) == "string", "sys.is_file: Argument 'file' is not a string.")
return lfs.attributes(file, "mode") == "file"
end
-- Return whether the 'dir' exists and is a directory.
function is_dir(dir)
assert(type(dir) == "string", "sys.is_dir: Argument 'dir' is not a string.")
return lfs.attributes(dir, "mode") == "directory"
end
-- Return the current working directory
function current_dir()
local dir, err = lfs.currentdir()
if not dir then return nil, err end
return dir
end
-- Return an iterator over the directory 'dir'.
-- If 'dir' doesn't exist or is not a directory, return nil and error message.
function get_directory(dir)
dir = dir or current_dir()
assert(type(dir) == "string", "sys.get_directory: Argument 'dir' is not a string.")
if is_dir(dir) then
return lfs.dir(dir)
else
return nil, "Error: '".. dir .. "' is not a directory."
end
end
-- Extract file or directory name from its path.
function extract_name(path)
assert(type(path) == "string", "sys.extract_name: Argument 'path' is not a string.")
if is_root(path) then return path end
path = remove_trailing(path)
path = path:gsub("^.*" .. path_separator(), "")
return path
end
-- Return parent directory of the 'path' or nil if there's no parent directory.
-- If 'path' is a path to file, return the directory the file is in.
function parent_dir(path)
assert(type(path) == "string", "sys.parent_dir: Argument 'path' is not a string.")
path = remove_curr_dir_dots(path)
path = remove_trailing(path)
local dir = path:gsub(utils.escape_magic(extract_name(path)) .. "$", "")
if dir == "" then
return nil
else
return make_path(dir)
end
end
-- Returns the table of all parent directories of 'path' up to the directory
-- specified by 'boundary_path' (exclusive).
function parents_up_to(path, boundary_path)
assert(type(path) == "string", "sys.parents_up_to: Argument 'path' is not a string.")
assert(type(boundary_path) == "string", "sys.parents_up_to: Argument 'boundary_path' is not a string.")
boundary_path = remove_trailing(boundary_path)
-- helper function to recursively collect the parent directories
local function collect_parents(_path, _parents)
local _parent = parent_dir(_path)
if _parent and _parent ~= boundary_path then
table.insert(_parents, _parent)
return collect_parents(_parent, _parents)
else
return _parents
end
end
return collect_parents(path, {})
end
-- Compose path composed from specified parts or current
-- working directory when no part specified.
function make_path(...)
-- arg is deprecated in lua 5.2 in favor of table.pack we mimic here
local arg = {n=select('#',...),...}
local parts = arg
assert(type(parts) == "table", "sys.make_path: Argument 'parts' is not a table.")
local path, err
if parts.n == 0 then
path, err = current_dir()
else
path, err = table.concat(parts, path_separator())
end
if not path then return nil, err end
-- squeeze repeated occurences of a file separator
path = path:gsub(path_separator() .. "+", path_separator())
-- remove unnecessary trailing path separator
path = remove_trailing(path)
return path
end
-- Return absolute path from 'path'
function abs_path(path)
assert(type(path) == "string", "sys.get_abs_path: Argument 'path' is not a string.")
if is_abs(path) then return path end
local cur_dir, err = current_dir()
if not cur_dir then return nil, err end
return make_path(cur_dir, path)
end
-- Returns path to the temporary directory of OS.
function tmp_dir()
return os.getenv("TMPDIR") or os.getenv("TEMP") or os.getenv("TMP") or "/tmp"
end
-- Returns temporary file (or directory) path (with optional prefix).
function tmp_name(prefix)
prefix = prefix or ""
assert(type(prefix) == "string", "sys.tmp_name: Argument 'prefix' is not a string.")
return make_path(tmp_dir(), prefix .. "luadist_" .. utils.rand(10000000000))
end
-- Return table of all paths in 'dir'
function get_file_list(dir)
dir = dir or current_dir()
assert(type(dir) == "string", "sys.get_directory: Argument 'dir' is not a string.")
if not exists(dir) then return nil, "Error getting file list of '" .. dir .. "': directory doesn't exist." end
local function collect(path, all_paths)
for item in get_directory(path) do
local item_path = make_path(path, item)
local _, last = item_path:find(dir .. path_separator(), 1, true)
local path_to_insert = item_path:sub(last + 1)
if is_file(item_path) then
table.insert(all_paths, path_to_insert)
elseif is_dir(item_path) and item ~= "." and item ~= ".." then
table.insert(all_paths, path_to_insert)
collect(item_path, all_paths)
end
end
end
local all_paths = {}
collect(dir, all_paths)
return all_paths
end
-- Return time of the last modification of 'file'.
function last_modification_time(file)
assert(type(file) == "string", "sys.last_modification_time: Argument 'file' is not a string.")
return lfs.attributes(file, "modification")
end
-- Return the current time (in seconds since epoch).
function current_time()
return os.time()
end
-- Change the current working directory and return 'true' and previous working
-- directory on success and 'nil' and error message on error.
function change_dir(dir_name)
assert(type(dir_name) == "string", "sys.change_dir: Argument 'dir_name' is not a string.")
local prev_dir = current_dir()
local ok, err = lfs.chdir(dir_name)
if ok then
return ok, prev_dir
else
return nil, err
end
end
-- Make a new directory, making also all of its parent directories that doesn't exist.
function make_dir(dir_name)
assert(type(dir_name) == "string", "sys.make_dir: Argument 'dir_name' is not a string.")
if exists(dir_name) then
return true
else
local par_dir = parent_dir(dir_name)
if par_dir then
local ok, err = make_dir(par_dir)
if not ok then return nil, err end
end
return lfs.mkdir(dir_name)
end
end
-- Move file (or directory) to the destination directory
function move_to(file_or_dir, dest_dir)
assert(type(file_or_dir) == "string", "sys.move_to: Argument 'file_or_dir' is not a string.")
assert(type(dest_dir) == "string", "sys.move_to: Argument 'dest_dir' is not a string.")
assert(is_dir(dest_dir), "sys.move_to: Destination '" .. dest_dir .."' is not a directory.")
-- Extract file/dir name from its path
local file_or_dir_name = extract_name(file_or_dir)
return os.rename(file_or_dir, make_path(dest_dir, file_or_dir_name))
end
-- rename file (or directory) to the new name.
function rename(file, new_name)
assert(type(file) == "string", "sys.rename: Argument 'file' is not a string.")
assert(type(new_name) == "string", "sys.rename: Argument 'new_name' is not a string.")
assert(not exists(new_name), "sys.rename: desired filename already exists.")
return os.rename(file, new_name)
end
-- Copy 'source' to the destination directory 'dest_dir'.
-- If 'source' is a directory, then recursive copying is used.
-- For non-recursive copying of directories use the make_dir() function.
function copy(source, dest_dir)
assert(type(source) == "string", "sys.copy: Argument 'file_or_dir' is not a string.")
assert(type(dest_dir) == "string", "sys.copy: Argument 'dest_dir' is not a string.")
assert(is_dir(dest_dir), "sys.copy: destination '" .. dest_dir .."' is not a directory.")
if cfg.arch == "Windows" then
if is_dir(source) then
make_dir(make_path(dest_dir, extract_name(source)))
return exec("xcopy /E /I /Y /Q " .. quote(source) .. " " .. quote(dest_dir .. "\\" .. extract_name(source)))
else
return exec("copy /Y " .. quote(source) .. " " .. quote(dest_dir))
end
else
if is_dir(source) then
return exec("cp -fRH " .. quote(source) .. " " .. quote(dest_dir))
else
return exec("cp -fH " .. quote(source) .. " " .. quote(dest_dir))
end
end
end
-- Delete the specified file or directory
function delete(path)
assert(type(path) == "string", "sys.delete: Argument 'path' is not a string.")
assert(is_abs(path), "sys.delete: Argument 'path' is not an absolute path.")
if cfg.arch == "Windows" then
if not exists(path) then
return true
elseif is_file(path) then
return os.remove(path)
else
return exec("rd /S /Q " .. quote(path))
end
else
return exec("rm -rf " .. quote(path))
end
end

View File

@ -1,151 +0,0 @@
-- System functions
module ("dist.utils", package.seeall)
local sys = require "dist.sys"
-- Returns a deep copy of 'table' with reference to the same metadata table.
-- Source: http://lua-users.org/wiki/CopyTable
function deepcopy(object)
local lookup_table = {}
local function _copy(object)
if type(object) ~= "table" then
return object
elseif lookup_table[object] then
return lookup_table[object]
end
local new_table = {}
lookup_table[object] = new_table
for index, value in pairs(object) do
new_table[_copy(index)] = _copy(value)
end
return setmetatable(new_table, getmetatable(object))
end
return _copy(object)
end
-- Return deep copy of table 'array', containing only items for which 'predicate_fn' returns true.
function filter(array, predicate_fn)
assert(type(array) == "table", "utils.filter: Argument 'array' is not a table.")
assert(type(predicate_fn) == "function", "utils.filter: Argument 'predicate_fn' is not a function.")
local filtered = {}
for _,v in pairs(array) do
if predicate_fn(v) == true then table.insert(filtered, deepcopy(v)) end
end
return filtered
end
-- Return deep copy of table 'array', sorted according to the 'compare_fn' function.
function sort(array, compare_fn)
assert(type(array) == "table", "utils.sort: Argument 'array' is not a table.")
assert(type(compare_fn) == "function", "utils.sort: Argument 'compare_fn' is not a function.")
local sorted = deepcopy(array)
table.sort(sorted, compare_fn)
return sorted
end
-- Return whether the 'value' is in the table 'tbl'.
function contains(tbl, value)
assert(type(tbl) == "table", "utils.contains: Argument 'tbl' is not a table.")
for _,v in pairs(tbl) do
if v == value then return true end
end
return false
end
-- Return single line string consisting of values in 'tbl' separated by comma.
-- Used for printing the dependencies/provides/conflicts.
function table_tostring(tbl, label)
assert(type(tbl) == "table", "utils.table_tostring: Argument 'tbl' is not a table.")
local str = ""
for k,v in pairs(tbl) do
if type(v) == "table" then
str = str .. table_tostring(v, k)
else
if label ~= nil then
str = str .. tostring(v) .. " [" .. tostring(label) .. "]" .. ", "
else
str = str .. tostring(v) .. ", "
end
end
end
return str
end
-- Return table made up from values of the string, separated by separator.
function make_table(str, separator)
assert(type(str) == "string", "utils.make_table: Argument 'str' is not a string.")
assert(type(separator) == "string", "utils.make_table: Argument 'separator' is not a string.")
local tbl = {}
for val in str:gmatch("(.-)" .. separator) do
table.insert(tbl, val)
end
local last_val = str:gsub(".-" .. separator, "")
if last_val and last_val ~= "" then
table.insert(tbl, last_val)
end
return tbl
end
-- Return whether the 'cache_timeout' for 'file' has expired.
function cache_timeout_expired(cache_timeout, file)
assert(type(cache_timeout) == "number", "utils.cache_timeout_expired: Argument 'cache_timeout' is not a number.")
assert(type(file) == "string", "utils.cache_timeout_expired: Argument 'file' is not a string.")
return sys.last_modification_time(file) + cache_timeout < sys.current_time()
end
-- Return the string 'str', with all magic (pattern) characters escaped.
function escape_magic(str)
assert(type(str) == "string", "utils.escape: Argument 'str' is not a string.")
local escaped = str:gsub('[%-%.%+%[%]%(%)%^%%%?%*%^%$]','%%%1')
return escaped
end
-- Return the boolean representation of an 'arg'.
function to_boolean(arg)
return not not arg
end
math.randomseed(os.time())
-- Return pseudo-random number in range [0, 1], [1, n] or [n, m].
function rand(...)
return math.random(...)
end
-- Perform check of system dependency, which isn't provided in the LuaDist
-- installation itself and if it is missing, print instructions how
-- to install it. The 'command' is used for testing, 'name' when printing
-- information to the user.
function system_dependency_available(name, command)
assert(type(name) == "string", "utils.system_dependency_available: Argument 'name' is not a string.")
assert(type(command) == "string", "utils.system_dependency_available: Argument 'command' is not a string.")
if not sys.exec(command) then
print("Error: command '" .. name .. "' not found on system. See installation instructions at\nhttps://github.com/LuaDist/Repository/wiki/Installation-of-System-Dependencies")
return false
end
return true
end
-- Obtain LuaDist location by checking available package locations
function get_luadist_location()
local paths = {}
local path = package.path:gsub("([^;]+)", function(c) table.insert(paths, c) end)
for _, path in pairs(paths) do
if (sys.is_abs(path) and path:find("[/\\]lib[/\\]lua[/\\]%?.lua$")) then
-- Remove path to lib/lua
path = path:gsub("[/\\]lib[/\\]lua[/\\]%?.lua$", "")
-- Clean the path up a bit
path = path:gsub("[/\\]bin[/\\]%.[/\\]%.%.", "")
path = path:gsub("[/\\]bin[/\\]%.%.", "")
return path
end
end
return nil
end

View File

@ -1,5 +0,0 @@
require 'git.util'
require 'git.objects'
require 'git.pack'
require 'git.repo'
require 'git.protocol'

View File

@ -1,121 +0,0 @@
local util = require 'git.util'
local assert, next, io, print, os, type, string, pairs, tostring =
assert, next, io, print, os, type, string, pairs, tostring
local join_path = git.util.join_path
local require = require
local isPosix = package.config:sub(1,1) == '/' -- wild guess
module(...)
Commit = {}
Commit.__index = Commit
function Commit:tree()
return self.repo:tree(self.tree_sha)
end
function Commit:checkout(path)
assert(path, 'path argument missing')
self:tree():checkoutTo(path)
end
Tree = {}
Tree.__index = function (t,k)
if Tree[k] then return Tree[k] end
return t:entry(k)
end
function Tree:entries()
return function(t, n)
local n, entry = next(t, n)
if entry then
local object
if entry.type == 'tree' then
object = self.repo:tree(entry.id)
elseif entry.type == 'blob' then
object = self.repo:blob(entry.id)
object.mode = entry.mode
elseif entry.type == 'commit' then
-- this is possibly a commit in a submodule,
-- do not retrieve it from current repo
object = entry
else
error('Unknown entry type: ' .. entry.type)
end
return n, entry.type, object
end
end, self._entries
end
function Tree:entry(n)
local e = self._entries[n]
if not e then return end
if e.type == 'tree' then
return self.repo:tree(e.id)
elseif e.type == 'commit' then
return self.repo:commit(e.id)
elseif e.type == 'blob' then
return self.repo:blob(e.id)
else
error('Unknown entry type: ' .. e.type)
end
end
function Tree:walk(func, path)
path = path or '.'
assert(type(func) == "function", "argument is not a function")
local function walk(tree, path)
for name, type, entry in tree:entries() do
local entry_path = join_path(path, name)
func(entry, entry_path, type)
if type == "tree" then
walk(entry, entry_path)
end
end
end
walk(self, path)
end
function Tree:checkoutTo(path)
util.make_dir(path)
self:walk(function (entry, entry_path, type)
if type == 'tree' then
util.make_dir(entry_path)
elseif type == 'blob' then
local out = assert(io.open(entry_path, 'wb'))
out:write(entry:content())
out:close()
if isPosix then
local mode = entry.mode:sub(-3,-1) -- fixme: is this ok?
local cmd = 'chmod '..mode..' "'..entry_path..'"'
os.execute(cmd)
end
elseif type == 'commit' then
-- this is a submodule referencing a commit,
-- make a directory for it
util.make_dir(entry_path)
else
error('Unknown entry type: ', type)
end
end, path)
end
Blob = {}
Blob.__index = Blob
function Blob:content()
if self.stored then
local f = self.repo:raw_object(self.id)
local ret = f:read('*a') or ""
f:close()
return ret
else
return self.data
end
end

View File

@ -1,316 +0,0 @@
local io = io
local core = require 'git.core'
local assert, pcall, print, select, setmetatable, string, type, unpack =
assert, pcall, print, select, setmetatable, string, type, unpack
local ord = string.byte
local fmt = string.format
local concat, insert = table.concat, table.insert
local band = core.band
local rshift, lshift = core.rshift, core.lshift
local to_hex = git.util.to_hex
local from_hex = git.util.from_hex
local object_sha = git.util.object_sha
local binary_sha = git.util.binary_sha
local readable_sha = git.util.readable_sha
local tmpfile = git.util.tmpfile
local reader = git.util.reader
module(...)
-- read git/Documentation/technical/pack-format.txt for some inspiration
-- 1 = commit, 2 = tree ...
local types = {'commit', 'tree', 'blob', 'tag', '???', 'ofs_delta', 'ref_delta'}
-- read a 4 byte unsigned integer stored in network order
local function read_int(f)
local s = f:read(4)
local a,b,c,d = s:byte(1,4)
return a*256^3 + b*256^2 + c*256 + d
end
-- read in the type and file length
local function read_object_header(f)
local b = ord(f:read(1))
local type = band(rshift(b, 4), 0x7)
local len = band(b, 0xF)
local ofs = 0
while band(b, 0x80) ~= 0 do
b = ord(f:read(1))
len = len + lshift(band(b, 0x7F), ofs * 7 + 4)
ofs = ofs + 1
end
return len, type
end
-- reads in the delta header and returns the offset where original data is stored
local function read_delta_header(f)
local b = ord(f:read(1))
local offset = band(b, 0x7F)
while band(b, 0x80) ~= 0 do
offset = offset + 1
b = ord(f:read(1))
offset = lshift(offset, 7) + band(b, 0x7F)
end
return offset
end
-- read just enough of file `f` to uncompress `size` bytes
local function uncompress_by_len(f, size)
local z = core.inflate()
local chunks = {}
local CHUNK_SIZE = 1024
local curr_pos = f:seek()
local inflated, eof, total
-- read until end of zlib-compresed stream
while not eof do
local data = f:read(CHUNK_SIZE)
inflated, eof, total = z(data)
insert(chunks, inflated)
end
-- repair the current position in stream
f:seek('set', curr_pos + total)
return concat(chunks)
end
-- uncompress the object from the current location in `f`
local function unpack_object(f, len, type)
local data = uncompress_by_len(f, len)
return data, len, type
end
-- returns a size value encoded in delta data
local function delta_size(f)
local size = 0
local i = 0
repeat
local b = ord(f:read(1))
size = size + lshift(band(b, 0x7F), i)
i = i + 7
until band(b, 0x80) == 0
return size
end
-- returns a patched object from string `base` according to `delta` data
local function patch_object(base, delta, base_type)
-- insert delta codes into temporary file
local df = reader(delta)
-- retrieve original and result size (for checks)
local orig_size = delta_size(df)
assert(#base == orig_size, fmt('#base(%d) ~= orig_size(%d)', #base, orig_size))
local result_size = delta_size(df)
local size = result_size
local result = {}
-- process the delta codes
local cmd = df:read(1)
while cmd do
cmd = ord(cmd)
if cmd == 0 then
error('unexpected delta code 0')
elseif band(cmd, 0x80) ~= 0 then -- copy a selected part of base data
local cp_off, cp_size = 0, 0
-- retrieve offset
if band(cmd, 0x01) ~= 0 then cp_off = ord(df:read(1)) end
if band(cmd, 0x02) ~= 0 then cp_off = cp_off + ord(df:read(1))*256 end
if band(cmd, 0x04) ~= 0 then cp_off = cp_off + ord(df:read(1))*256^2 end
if band(cmd, 0x08) ~= 0 then cp_off = cp_off + ord(df:read(1))*256^3 end
-- retrieve size
if band(cmd, 0x10) ~= 0 then cp_size = ord(df:read(1)) end
if band(cmd, 0x20) ~= 0 then cp_size = cp_size + ord(df:read(1))*256 end
if band(cmd, 0x40) ~= 0 then cp_size = cp_size + ord(df:read(1))*256^2 end
if cp_size == 0 then cp_size = 0x10000 end
if cp_off + cp_size > #base or cp_size > size then break end
-- get the data and append it to result
local data = base:sub(cp_off + 1, cp_off + cp_size)
insert(result, data)
size = size - cp_size
else -- insert new data
if cmd > size then break end
local data = df:read(cmd)
insert(result, data)
size = size - cmd
end
cmd = df:read(1)
end
df:close()
result = concat(result)
assert(#result == result_size, fmt('#result(%d) ~= result_size(%d)', #result, result_size))
return result, result_size, base_type
end
Pack = {}
Pack.__index = Pack
-- read an object from the current location in pack, or from a specific `offset`
-- if specified
function Pack:read_object(offset, ignore_data)
local f = self.pack_file
if offset then
f:seek('set', offset)
end
local curr_pos = f:seek()
local len, type = read_object_header(f)
if type < 5 then -- commit, tree, blob, tag
return unpack_object(f, len, type)
elseif type == 6 then -- ofs_delta
local offset = read_delta_header(f)
local delta_data = uncompress_by_len(f, len)
if not ignore_data then
-- the offset is negative from the current location
local base, base_len, base_type = self:read_object(curr_pos - offset)
return patch_object(base, delta_data, base_type)
end
elseif type == 7 then -- ref_delta
local sha = f:read(20)
local delta_data = uncompress_by_len(f, len)
if not ignore_data then
-- lookup the object in the pack by sha
-- FIXME: maybe lookup in repo/other packs
local base_offset = self.index[binary_sha(sha)]
local base, base_len, base_type = self:read_object(base_offset)
return patch_object(base, delta_data, base_type)
end
else
error('unknown object type: '..type)
end
end
-- returns true if this pack contains the given object
function Pack:has_object(sha)
return self.index[binary_sha(sha)] ~= nil
end
-- if the object name `sha` exists in the pack, returns a temporary file with the
-- object content, length and type, otherwise returns nil
function Pack:get_object(sha)
local offset = self.index[binary_sha(sha)]
if not offset then
print('!!! Failed to find object', readable_sha(sha))
end
local data, len, type = self:read_object(offset)
print(readable_sha(sha), len, type, data)
local f = tmpfile()
f:write(data)
f:seek('set', 0)
return f, len, types[type]
end
function Pack:unpack(repo)
for i=1, self.nobjects do
local offset = self.offsets[i]
local data, len, type = self:read_object(offset)
repo:store_object(data, len, types[type])
end
end
-- parses the index
function Pack:parse_index(index_file)
local f = index_file
local head = f:read(4)
assert(head == '\255tOc', "Incorrect header: " .. head)
local version = read_int(f)
assert(version == 2, "Incorrect version: " .. version)
-- first the fanout table (how many objects are in the index, whose
-- first byte is below or equal to i)
local fanout = {}
for i=0, 255 do
local nobjs = read_int(f)
fanout[i] = nobjs
end
-- the last element in fanout is the number of all objects in index
local count = fanout[255]
-- then come the sorted object names (=sha hash)
local tmp = {}
for i=1,count do
local sha = f:read(20)
tmp[i] = { sha = sha }
end
-- then the CRCs (assume ok, skip them)
for i=1, count do
local crc = f:read(4)
end
-- then come the offsets - read just the 32bit ones, does not handle packs > 2G
for i=1, count do
local offset = read_int(f)
tmp[i].offset = offset
end
-- construct the lookup table
local lookup = {}
for i=1, count do
lookup[tmp[i].sha] = tmp[i].offset
end
self.index = lookup
end
-- constructs the index/offsets if the index file is missing
function Pack:construct_index(path)
local index = {}
for i=1, self.nobjects do
local offset = self.offsets[i]
local data, len, type = self:read_object(offset)
local sha = object_sha(data, len, types[type])
index[binary_sha(sha)] = offset
end
self.index = index
end
function Pack:close()
self.pack_file:close()
end
function Pack.open(path)
local fp = assert(io.open(path, 'rb')) -- stays open
-- read the pack header
local head = fp:read(4)
assert(head == 'PACK', "Incorrect header: " .. head)
local version = read_int(fp)
assert(version == 2, "Incorrect version: " .. version)
local nobj = read_int(fp)
local pack = setmetatable({
offsets = {},
nobjects = nobj,
pack_file = fp,
}, Pack)
-- fill the offsets by traversing through the pack
for i=1,nobj do
pack.offsets[i] = fp:seek()
-- ignore the object data, we only need the offset in the pack
pack:read_object(nil, true)
end
-- read the index
local fi = io.open((path:gsub('%.pack$', '.idx')), 'rb')
if fi then
pack:parse_index(fi)
fi:close()
else
pack:construct_index(path)
end
return pack
end
return Pack

View File

@ -1,188 +0,0 @@
local socket = require 'socket'
local urllib = require 'socket.url'
local lfs = require 'lfs'
local Repo = git.repo.Repo
local Pack = git.pack.Pack
local join_path = git.util.join_path
local parent_dir = git.util.parent_dir
local make_dir = git.util.make_dir
local correct_separators = git.util.correct_separators
local assert, error, getmetatable, io, os, pairs, print, require, string, tonumber =
assert, error, getmetatable, io, os, pairs, print, require, string, tonumber
local _VERSION, newproxy = _VERSION, newproxy
module(...)
local GIT_PORT = 9418
local function git_connect(host)
local sock = assert(socket.connect(host, GIT_PORT))
local gitsocket = {}
function gitsocket:send(data)
if not data then -- flush packet
sock:send('0000')
else
local len = #data + 4
len = string.format("%04x", len)
assert(sock:send(len .. data))
end
end
function gitsocket:receive()
local len = assert(sock:receive(4))
len = tonumber(len, 16)
if len == 0 then return end -- flush packet
local data = assert(sock:receive(len - 4))
return data
end
function gitsocket:close()
sock:close()
end
return gitsocket
end
local function addFinalizer(object, finalizer)
if _VERSION <= "Lua 5.1" then
local gc = newproxy(true)
getmetatable(gc).__gc = finalizer
object.__gc = gc
else
local mt = getmetatable(object)
if mt then mt.__gc = finalizer
else setmetatable(object, {__gc = finalizer})
end
end
end
local function git_fetch(host, path, repo, head, supress_progress)
local s = git_connect(host)
s:send('git-upload-pack '..path..'\0host='..host..'\0')
local refs, refsbyname = {}, {}
repeat
local ref = s:receive()
if ref then
local sha, name = ref:sub(1,40), ref:sub(42, -2)
refs[sha] = name
refsbyname[name] = sha
end
until not ref
local wantedSha
local headsha = head and refsbyname[head]
for sha, ref in pairs(refs) do
-- we implicitly want this ref
local wantObject = true
-- unless we ask for a specific head
if headsha then
if sha ~= headsha then
wantObject = false
else
wantedSha = sha
end
end
-- or we already have it
if repo and repo:has_object(sha) then
wantObject = false
end
if wantObject then
s:send('want '..sha..' multi_ack_detailed side-band-64k ofs-delta\n')
end
end
if head and not wantedSha then
error("Server does not have "..head)
end
s:send('deepen 1')
s:send()
while s:receive() do end
s:send('done\n')
assert(s:receive() == "NAK\n")
local packname = os.tmpname() .. '.pack'
local packfile = assert(io.open(packname, 'wb'))
repeat
local got = s:receive()
if got then
-- get sideband channel, 1=pack data, 2=progress, 3=error
local cmd = string.byte(got:sub(1,1))
local data = got:sub(2)
if cmd == 1 then
packfile:write(data)
elseif cmd == 2 then
if not supress_progress then io.write(data) end
else
error(data)
end
end
until not got
packfile:close()
s:close()
local pack = Pack.open(packname)
if repo then
pack:unpack(repo)
repo.isShallow = true
if wantedSha then
local headfile = correct_separators(join_path(repo.dir, head))
assert(make_dir(parent_dir(headfile)))
local f = assert(io.open(headfile, 'wb'))
f:write(wantedSha)
f:close()
end
end
addFinalizer(pack, function()
os.remove(packname)
end)
return pack, wantedSha
end
function fetch(url, repo, head, supress_progress)
if repo then assert(getmetatable(repo) == Repo, "arg #2 is not a repository") end
url = urllib.parse(url)
if url.scheme == 'git' then
local pack, sha = git_fetch(url.host, url.path, repo, head, supress_progress)
return pack, sha
else
error('unsupported scheme: '..url.scheme)
end
end
function remotes(url)
-- TODO: refactor common code
url = assert(urllib.parse(url))
if url.scheme ~= 'git' then
error('unsupported scheme: '..url.scheme)
end
local host, path = url.host, url.path
local s = git_connect(host)
s:send('git-upload-pack '..path..'\0host='..host..'\0')
local remote = {}
repeat
local ref = s:receive()
if ref then
local sha, name = ref:sub(1,40), ref:sub(42, -2)
remote[name] = sha
end
until not ref
s:close()
return remote
end

View File

@ -1,283 +0,0 @@
local util = require 'git.util'
local objects = require 'git.objects'
local core = require 'git.core'
local pack = require 'git.pack'
local join_path = util.join_path
local decompressed = util.decompressed
local read_until_nul = util.read_until_nul
local to_hex = util.to_hex
local object_sha = util.object_sha
local readable_sha = util.readable_sha
local deflate = core.deflate
local lfs = require 'lfs'
local assert, error, io, ipairs, print, os, setmetatable, string, table =
assert, error, io, ipairs, print, os, setmetatable, string, table
module(...)
Repo = {}
Repo.__index = Repo
-- retrieves an object identified by `sha` from the repository or its packs
-- returns a file-like object (supports 'read', 'seek' and 'close'), the size
-- of the object and its type
-- errors when the object does not exist
function Repo:raw_object(sha)
-- first, look in 'objects' directory
-- first byte of sha is the directory, the rest is name of object file
sha = readable_sha(sha)
local dir = sha:sub(1,2)
local file = sha:sub(3)
local path = join_path(self.dir, 'objects', dir, file)
if not lfs.attributes(path, 'size') then
-- then, try to look in packs
for _, pack in ipairs(self.packs) do
local obj, len, typ = pack:get_object(sha)
if obj then
return obj, len, typ
end
end
error('Object not found in object neither in packs: '..sha)
else
-- the objects are zlib compressed
local f = decompressed(path)
-- retrieve the type and length - <type> SP <len> \0 <data...>
local content = read_until_nul(f)
local typ, len = content:match('(%w+) (%d+)')
return f, len, typ
end
end
--- Store a new object into the repository in `objects` directory.
-- @param data A string containing the contents of the new file.
-- @param len The length of the data.
-- @param type One of 'commit', 'blob', 'tree', 'tag'
function Repo:store_object(data, len, type)
local sha = readable_sha(object_sha(data, len, type))
local dir = sha:sub(1,2)
local file = sha:sub(3)
util.make_dir(join_path(self.dir, 'objects', dir))
local path = join_path(self.dir, 'objects', dir, file)
local fo = assert(io.open(path, 'wb'))
local header = type .. ' ' .. len .. '\0'
local compressed = deflate()(header .. data, "finish")
fo:write(compressed)
fo:close()
end
local function resolvetag(f)
local tag
local line = f:read()
while line do
tag = line:match('^object (%x+)$')
if tag then break end
line = f:read()
end
f:close()
return tag
end
function Repo:commit(sha)
local f, len, typ = self:raw_object(sha)
while typ == 'tag' do
sha = assert(resolvetag(f), 'could not parse tag for '..readable_sha(sha))
f, len, typ = self:raw_object(sha)
end
assert(typ == 'commit', string.format('%s (%s) is not a commit', sha, typ))
local commit = { id = sha, repo = self, stored = true, parents = {} }
repeat
local line = f:read()
if not line then break end
local space = line:find(' ') or 0
local word = line:sub(1, space - 1)
local afterSpace = line:sub(space + 1)
if word == 'tree' then
commit.tree_sha = afterSpace
elseif word == 'parent' then
table.insert(commit.parents, afterSpace)
elseif word == 'author' then
commit.author = afterSpace
elseif word == 'committer' then
commit.committer = afterSpace
elseif commit.message then
table.insert(commit.message, line)
elseif line == '' then
commit.message = {}
end
until false -- ends with break
f:close()
commit.message = table.concat(commit.message, '\n')
return setmetatable(commit, objects.Commit)
end
function Repo:tree(sha)
local f, len, typ = self:raw_object(sha)
assert(typ == 'tree', string.format('%s (%s) is not a tree', sha, typ))
local tree = { id = sha, repo = self, stored = true, _entries = {} }
while true do
local info = read_until_nul(f)
if not info then break end
local entry_sha = to_hex(f:read(20))
local mode, name = info:match('^(%d+)%s(.+)$')
local entry_type = 'blob'
if mode == '40000' then
entry_type = 'tree'
elseif mode == '160000' then
entry_type = 'commit'
end
tree._entries[name] = { mode = mode, id = entry_sha, type = entry_type }
end
f:close()
return setmetatable(tree, objects.Tree)
end
-- retrieves a Blob
function Repo:blob(sha)
local f, len, typ = self:raw_object(sha)
f:close() -- can be reopened in Blob:content()
assert(typ == 'blob', string.format('%s (%s) is not a blob', sha, typ))
return setmetatable({
id = sha,
len = len,
repo = self,
stored = true }, objects.Blob)
end
function Repo:head()
return self:commit(self.refs.HEAD)
end
function Repo:has_object(sha)
local dir = sha:sub(1,2)
local file = sha:sub(3)
local path = join_path(self.dir, 'objects', dir, file)
if lfs.attributes(path, 'size') then return true end
for _, pack in ipairs(self.packs) do
local has = pack:has_object(sha)
if has then return true end
end
return false
end
function Repo:checkout(sha, target)
if not target then target = self.workDir end
assert(target, 'target directory not specified')
local commit = self:commit(sha)
commit:checkout(target)
-- if the repo was checked out using the deepen command (one level of history only)
-- mark the commit's parent as shalow, that is it has no history
if self.isShallow then
-- if it has a parent, mark it shallow
if commit.parents[1] then
local f = assert(io.open(self.dir .. '/shallow', "w"))
f:write(commit.parents[1], '\n')
f:close()
end
end
end
function Repo:close()
for _, pack in ipairs(self.packs) do
pack:close()
end
end
function create(dir)
if not dir:match('%.git.?$') then
dir = join_path(dir, '.git')
end
util.make_dir(dir)
util.make_dir(dir .. '/branches')
util.make_dir(dir .. '/hooks')
util.make_dir(dir .. '/info')
util.make_dir(dir .. '/objects/info')
util.make_dir(dir .. '/objects/pack')
util.make_dir(dir .. '/refs/heads')
util.make_dir(dir .. '/refs/tags')
util.make_dir(dir .. '/refs/remotes')
do
local f = assert(io.open(dir .. "/HEAD", "w"))
f:write("ref: refs/heads/master\n")
f:close()
end
local refs = {}
local packs = {}
return setmetatable({
dir = dir,
refs = refs,
packs = packs,
}, Repo)
end
-- opens a repository located in working directory `dir` or directly a .git repo
function open(dir)
local workDir = dir
if not dir:match('%.git.?$') then
dir = join_path(dir, '.git')
else
workDir = nil -- no working directory, working directly with repo
end
local refs = {}
for _,d in ipairs{'refs/heads', 'refs/tags'} do
for fn in lfs.dir(join_path(dir, d)) do
if fn ~= '.' and fn ~= '..' then
local path = join_path(dir, d, fn)
local f = assert(io.open(path), 'rb')
local ref = f:read()
refs[join_path(d, fn)] = ref
f:close()
end
end
end
local packs = {}
for fn in lfs.dir(join_path(dir, 'objects/pack')) do
if fn:match('%.pack$') then
local path = join_path(dir, 'objects/pack', fn)
table.insert(packs, pack.open(path))
end
end
local head = io.open(join_path(dir, 'HEAD'), 'rb')
if head then
local src = head:read()
local HEAD = src:match('ref: (.-)$')
refs.HEAD = refs[HEAD]
head:close()
end
return setmetatable({
dir = dir,
workDir = workDir,
refs = refs,
packs = packs,
}, Repo)
end
return Repo

View File

@ -1,233 +0,0 @@
local lfs = require 'lfs'
local core = require 'git.core'
local deflate = core.deflate
local inflate = core.inflate
local sha = core.sha
module(..., package.seeall)
local BUF_SIZE = 4096
local dirsep = package.config:sub(1,1)
-- replaces '/' path separators on Windows with the correct ones ('\\')
function correct_separators(path)
return path:gsub('/', dirsep)
end
-- joins several path components into a single path, uses system-specific directory
-- separator, cleans input, i.e. join_path('a/', 'b', 'c/') => 'a/b/c'
function join_path(...)
local n = select('#', ...)
local args = {...}
for i=1,n do
args[i] = args[i]:gsub(dirsep..'?$', '')
end
return table.concat(args, dirsep, 1, n)
end
-- Return the path with the all occurences of '/.' or '\.' (representing
-- the current directory) removed.
local function remove_curr_dir_dots(path)
while path:match(dirsep .. "%." .. dirsep) do -- match("/%./")
path = path:gsub(dirsep .. "%." .. dirsep, dirsep) -- gsub("/%./", "/")
end
return path:gsub(dirsep .. "%.$", "") -- gsub("/%.$", "")
end
-- Return whether the path is a root.
local function is_root(path)
return path:find("^[%u%U.]?:?[/\\]$")
end
-- Return the path with the unnecessary trailing separator removed.
local function remove_trailing(path)
if path:sub(-1) == dirsep and not is_root(path) then path = path:sub(1,-2) end
return path
end
-- Extract file or directory name from its path.
local function extract_name(path)
if is_root(path) then return path end
path = remove_trailing(path)
path = path:gsub("^.*" .. dirsep, "")
return path
end
-- Return the string 'str', with all magic (pattern) characters escaped.
local function escape_magic(str)
local escaped = str:gsub('[%-%.%+%[%]%(%)%^%%%?%*%^%$]','%%%1')
return escaped
end
-- Return parent directory of the 'path' or nil if there's no parent directory.
-- If 'path' is a path to file, return the directory the file is in.
function parent_dir(path)
path = remove_curr_dir_dots(path)
path = remove_trailing(path)
local dir = path:gsub(escape_magic(extract_name(path)) .. "$", "")
if dir == "" then
return nil
else
return remove_trailing(dir)
end
end
-- Make a new directory, making also all of its parent directories that doesn't exist.
function make_dir(path)
if lfs.attributes(path) then
return true
else
local par_dir = parent_dir(path)
if par_dir then
assert(make_dir(par_dir))
end
return lfs.mkdir(path)
end
end
-- Reader class
-- adapted from Penlight: https://raw.github.com/stevedonovan/Penlight/master/lua/pl/stringio.lua
local SR = {}
SR.__index = SR
function SR:_read(fmt)
local i,str = self.i,self.str
local sz = #str
if i > sz then return nil, "past end of file" end
local res
if fmt == '*l' or fmt == '*L' then
local idx = str:find('\n',i) or (sz+1)
res = str:sub(i,fmt == '*l' and idx-1 or idx)
self.i = idx+1
elseif fmt == '*a' then
res = str:sub(i)
self.i = sz+1
elseif fmt == '*n' then
local _,i2,i2,idx
_,idx = str:find ('%s*%d+',i)
_,i2 = str:find ('^%.%d+',idx+1)
if i2 then idx = i2 end
_,i2 = str:find ('^[eE][%+%-]*%d+',idx+1)
if i2 then idx = i2 end
local val = str:sub(i,idx)
res = tonumber(val)
self.i = idx+1
elseif type(fmt) == 'number' then
res = str:sub(i,i+fmt-1)
self.i = i + fmt
else
error("bad read format",2)
end
return res
end
function SR:read(...)
if select('#',...) == 0 then
return self:_read('*l')
else
local res, fmts = {},{...}
for i = 1, #fmts do
res[i] = self:_read(fmts[i])
end
return unpack(res)
end
end
function SR:seek(whence,offset)
local base
whence = whence or 'cur'
offset = offset or 0
if whence == 'set' then
base = 1
elseif whence == 'cur' then
base = self.i
elseif whence == 'end' then
base = #self.str
end
self.i = base + offset
return self.i
end
function SR:close() -- for compatibility only
end
--- create a file-like object for reading from a given string.
-- @param s The input string.
function reader(s)
return setmetatable({str=s,i=1},SR)
end
-- decompress the file and return a handle to temporary uncompressed file
function decompressed(path)
local fi = assert(io.open(path, 'rb'))
local result = {}
local z = inflate()
repeat
local str = fi:read(BUF_SIZE)
local data = z(str)
if type(data) == 'string' then
result[#result+1] = data
else print('!!!', data) end
until not str
fi:close()
return reader(table.concat(result))
end
-- reads until the byte \0, consumes it and returns the string up to the \0
function read_until_nul(f)
local t = {}
repeat
local c = f:read(1)
if c and c ~= '\0' then t[#t+1] = c end
until not c or c == '\0'
if #t > 0 then
return table.concat(t)
else
return nil
end
end
-- converts a string to lowercase hex
function to_hex(s)
return (s:gsub('.', function(c)
return string.format('%02x', string.byte(c))
end))
end
-- converts a string from hex to binary
function from_hex(s)
return (s:gsub('..', function(cc)
return string.char(tonumber(cc, 16))
end))
end
-- always returns readable (hex) hash
function readable_sha(s)
if #s ~= 40 then return to_hex(s)
else return s end
end
-- always returns binary hash
function binary_sha(s)
if #s ~= 20 then return from_hex(s)
else return s end
end
function object_sha(data, len, type)
local header = type .. ' ' .. len .. '\0'
local res = sha(header .. data)
return res
end
function deflate(data)
local c = deflate()
return c(data, "finish")
end

View File

@ -1,298 +0,0 @@
-----------------------------------------------------------------------------
-- LTN12 - Filters, sources, sinks and pumps.
-- LuaSocket toolkit.
-- Author: Diego Nehab
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module
-----------------------------------------------------------------------------
local string = require("string")
local table = require("table")
local base = _G
local _M = {}
if module then -- heuristic for exporting a global package table
ltn12 = _M
end
local filter,source,sink,pump = {},{},{},{}
_M.filter = filter
_M.source = source
_M.sink = sink
_M.pump = pump
-- 2048 seems to be better in windows...
_M.BLOCKSIZE = 2048
_M._VERSION = "LTN12 1.0.3"
-----------------------------------------------------------------------------
-- Filter stuff
-----------------------------------------------------------------------------
-- returns a high level filter that cycles a low-level filter
function filter.cycle(low, ctx, extra)
base.assert(low)
return function(chunk)
local ret
ret, ctx = low(ctx, chunk, extra)
return ret
end
end
-- chains a bunch of filters together
-- (thanks to Wim Couwenberg)
function filter.chain(...)
local arg = {...}
local n = select('#',...)
local top, index = 1, 1
local retry = ""
return function(chunk)
retry = chunk and retry
while true do
if index == top then
chunk = arg[index](chunk)
if chunk == "" or top == n then return chunk
elseif chunk then index = index + 1
else
top = top+1
index = top
end
else
chunk = arg[index](chunk or "")
if chunk == "" then
index = index - 1
chunk = retry
elseif chunk then
if index == n then return chunk
else index = index + 1 end
else base.error("filter returned inappropriate nil") end
end
end
end
end
-----------------------------------------------------------------------------
-- Source stuff
-----------------------------------------------------------------------------
-- create an empty source
local function empty()
return nil
end
function source.empty()
return empty
end
-- returns a source that just outputs an error
function source.error(err)
return function()
return nil, err
end
end
-- creates a file source
function source.file(handle, io_err)
if handle then
return function()
local chunk = handle:read(_M.BLOCKSIZE)
if not chunk then handle:close() end
return chunk
end
else return source.error(io_err or "unable to open file") end
end
-- turns a fancy source into a simple source
function source.simplify(src)
base.assert(src)
return function()
local chunk, err_or_new = src()
src = err_or_new or src
if not chunk then return nil, err_or_new
else return chunk end
end
end
-- creates string source
function source.string(s)
if s then
local i = 1
return function()
local chunk = string.sub(s, i, i+_M.BLOCKSIZE-1)
i = i + _M.BLOCKSIZE
if chunk ~= "" then return chunk
else return nil end
end
else return source.empty() end
end
-- creates rewindable source
function source.rewind(src)
base.assert(src)
local t = {}
return function(chunk)
if not chunk then
chunk = table.remove(t)
if not chunk then return src()
else return chunk end
else
table.insert(t, chunk)
end
end
end
function source.chain(src, f)
base.assert(src and f)
local last_in, last_out = "", ""
local state = "feeding"
local err
return function()
if not last_out then
base.error('source is empty!', 2)
end
while true do
if state == "feeding" then
last_in, err = src()
if err then return nil, err end
last_out = f(last_in)
if not last_out then
if last_in then
base.error('filter returned inappropriate nil')
else
return nil
end
elseif last_out ~= "" then
state = "eating"
if last_in then last_in = "" end
return last_out
end
else
last_out = f(last_in)
if last_out == "" then
if last_in == "" then
state = "feeding"
else
base.error('filter returned ""')
end
elseif not last_out then
if last_in then
base.error('filter returned inappropriate nil')
else
return nil
end
else
return last_out
end
end
end
end
end
-- creates a source that produces contents of several sources, one after the
-- other, as if they were concatenated
-- (thanks to Wim Couwenberg)
function source.cat(...)
local arg = {...}
local src = table.remove(arg, 1)
return function()
while src do
local chunk, err = src()
if chunk then return chunk end
if err then return nil, err end
src = table.remove(arg, 1)
end
end
end
-----------------------------------------------------------------------------
-- Sink stuff
-----------------------------------------------------------------------------
-- creates a sink that stores into a table
function sink.table(t)
t = t or {}
local f = function(chunk, err)
if chunk then table.insert(t, chunk) end
return 1
end
return f, t
end
-- turns a fancy sink into a simple sink
function sink.simplify(snk)
base.assert(snk)
return function(chunk, err)
local ret, err_or_new = snk(chunk, err)
if not ret then return nil, err_or_new end
snk = err_or_new or snk
return 1
end
end
-- creates a file sink
function sink.file(handle, io_err)
if handle then
return function(chunk, err)
if not chunk then
handle:close()
return 1
else return handle:write(chunk) end
end
else return sink.error(io_err or "unable to open file") end
end
-- creates a sink that discards data
local function null()
return 1
end
function sink.null()
return null
end
-- creates a sink that just returns an error
function sink.error(err)
return function()
return nil, err
end
end
-- chains a sink with a filter
function sink.chain(f, snk)
base.assert(f and snk)
return function(chunk, err)
if chunk ~= "" then
local filtered = f(chunk)
local done = chunk and ""
while true do
local ret, snkerr = snk(filtered, err)
if not ret then return nil, snkerr end
if filtered == done then return 1 end
filtered = f(done)
end
else return 1 end
end
end
-----------------------------------------------------------------------------
-- Pump stuff
-----------------------------------------------------------------------------
-- pumps one chunk from the source to the sink
function pump.step(src, snk)
local chunk, src_err = src()
local ret, snk_err = snk(chunk, src_err)
if chunk and ret then return 1
else return nil, src_err or snk_err end
end
-- pumps all data from a source to a sink, using a step function
function pump.all(src, snk, step)
base.assert(src and snk)
step = step or pump.step
while true do
local ret, err = step(src, snk)
if not ret then
if err then return nil, err
else return 1 end
end
end
end
return _M

View File

@ -1,212 +0,0 @@
--[[
lua_lexer_loose.lua.
Loose lexing of Lua code. See README.
WARNING: This code is preliminary and may have errors
in its current form.
(c) 2013 David Manura. MIT License.
--]]
local M = {}
-- based on LuaBalanced
local function match_string(s, pos)
pos = pos or 1
local posa = pos
local c = s:sub(pos,pos)
if c == '"' or c == "'" then
pos = pos + 1
while 1 do
pos = s:find("[" .. c .. "\\]", pos)
if not pos then return s:sub(posa), #s + 1 end -- not terminated string
if s:sub(pos,pos) == c then
local part = s:sub(posa, pos)
return part, pos + 1
else
pos = pos + 2
end
end
else
local sc = s:match("^%[(=*)%[", pos)
if sc then
local _; _, pos = s:find("%]" .. sc .. "%]", pos)
if not pos then return s:sub(posa), #s + 1 end -- not terminated string
local part = s:sub(posa, pos)
return part, pos + 1
else
return nil, pos
end
end
end
-- based on LuaBalanced
local function match_comment(s, pos)
pos = pos or 1
if s:sub(pos, pos+1) ~= '--' then
return nil, pos
end
pos = pos + 2
if s:sub(pos,pos) == '[' then
local partt, post = match_string(s, pos)
if partt then
return '--' .. partt, post
end
end
local part; part, pos = s:match('^([^\n]*\n?)()', pos)
return '--' .. part, pos
end
-- note: matches invalid numbers too (for example, 0x)
local function match_numberlike(s, pos)
local hex = s:match('^0[xX]', pos)
if hex then pos = pos + #hex end
local longint = (hex and '^%x+' or '^%d+') .. '[uU]?[lL][lL]'
local mantissa1 = hex and '^%x+%.?%x*' or '^%d+%.?%d*'
local mantissa2 = hex and '^%.%x+' or '^%.%d+'
local exponent = hex and '^[pP][+%-]?%x*' or '^[eE][+%-]?%d*'
local imaginary = '^[iI]'
local tok = s:match(longint, pos)
if not tok then
tok = s:match(mantissa1, pos) or s:match(mantissa2, pos)
if tok then
local tok2 = s:match(exponent, pos + #tok)
if tok2 then tok = tok..tok2 end
tok2 = s:match(imaginary, pos + #tok)
if tok2 then tok = tok..tok2 end
end
end
return tok and (hex or '') .. tok or hex
end
local function newset(s)
local t = {}
for c in s:gmatch'.' do t[c] = true end
return t
end
local function qws(s)
local t = {}
for k in s:gmatch'%S+' do t[k] = true end
return t
end
local sym = newset("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_")
local dig = newset('0123456789')
local name = "([_A-Za-z][_A-Za-z0-9]*)"
local op = newset('=~<>.+-*/%^#=<>;:,.{}[]()')
op['=='] = true
op['<='] = true
op['>='] = true
op['~='] = true
op['..'] = true
op['<<'] = true
op['>>'] = true
op['//'] = true
local is_keyword = qws[[
and break do else elseif end false for function if
in local nil not or repeat return
then true until while goto]]
function M.lex(code, f, pos)
local pos = pos or 1
local tok = code:match('^#![^\n]*\n', pos) -- shebang
if tok then f('Shebang', tok, 1) pos = pos + #tok end
while pos <= #code do
local p2, n2, n1, n3 = code:match('^%s*()((%S)(%S?))', pos)
if not p2 then assert(code:sub(pos):match('^%s*$')); break end
pos = p2
if sym[n1] then
local tok = code:match('^'..name, pos)
assert(tok)
if is_keyword[tok] then
f('Keyword', tok, pos)
else
f('Id', tok, pos)
end
pos = pos + #tok
elseif n2 == '--' then
local tok, pos2 = match_comment(code, pos)
assert(tok)
f('Comment', tok, pos)
pos = pos2
elseif n2 == '::' then
local tok = code:match('^(::%s*'..name..'%s*::)', pos)
if tok then
f('Label', tok, pos)
pos = pos + #tok
else
f('Unknown', code:sub(pos, pos+1), pos) -- unterminated label
pos = pos + 2
end
elseif n1 == '\'' or n1 == '\"' or n2 == '[[' or n2 == '[=' then
local tok = match_string(code, pos)
if tok then
f('String', tok, pos)
pos = pos + #tok
else
f('Unknown', code:sub(pos), pos) -- unterminated string
pos = #code + 1
end
elseif dig[n1] or (n1 == '.' and dig[n3]) then
local tok = match_numberlike(code, pos)
assert(tok)
f('Number', tok, pos)
pos = pos + #tok
elseif op[n2] then
if n2 == '..' and code:match('^%.', pos+2) then
tok = '...'
else
tok = n2
end
f('Keyword', tok, pos)
pos = pos + #tok
elseif op[n1] then
local tok = n1
f('Keyword', tok, pos)
pos = pos + #tok
else
f('Unknown', n1, pos)
pos = pos + 1
end
end
end
local Stream = {}
Stream.__index = Stream
function Stream:next(val)
if self._next then
local _next = self._next
self._next = nil
return _next
else
self._next = nil
return self.f()
end
end
function Stream:peek()
if self._next then
return self._next
else
local _next = self.f()
self._next = _next
return _next
end
end
function M.lexc(code, f, pos)
local yield = coroutine.yield
local func = coroutine.wrap(f or function()
M.lex(code, function(tag, name, pos)
-- skip Comment tags as they may arbitrarily split statements and affects their processing
if tag ~= 'Comment' then yield {tag=tag, name, lineinfo=pos} end
end, pos)
yield {tag='Eof', lineinfo = #code+1}
end)
return setmetatable({f=func}, Stream)
end
return M

View File

@ -1,337 +0,0 @@
--[[
lua_parser_loose.lua.
Loose parsing of Lua code. See README.
(c) 2013 David Manura. MIT License.
--]]
local PARSE = {}
local unpack = table.unpack or unpack
local LEX = require 'lua_lexer_loose'
--[[
Loose parser.
lx - lexer stream of Lua tokens.
f(event...) - callback function to send events to.
Events generated:
'Var', name, lineinfo - variable declaration that immediately comes into scope.
'VarSelf', name, lineinfo - same as 'Var' but for implicit 'self' parameter
in method definitions. lineinfo is zero-width space after '('
'VarNext', name, lineinfo - variable definition that comes into scope
upon next statement.
'VarInside', name, lineinfo - variable definition that comes into scope
inside following block. Used for control variables in 'for' statements.
'Id', name, lineinfo - reference to variable.
'String', name - string or table field.
'Scope', opt - beginning of scope block.
'EndScope', nil, lineinfo - end of scope block.
'FunctionCall', name, lineinfo - function call (in addition to other events).
'Function', name, lineinfo - function definition.
--]]
function PARSE.parse_scope(lx, f, level)
local cprev = {tag='Eof'}
-- stack of scopes.
local scopes = {{}}
for l = 2, (level or 1) do scopes[l] = {} end
local function scope_begin(opt, lineinfo, nobreak)
scopes[#scopes+1] = {}
f('Scope', opt, lineinfo, nobreak)
end
local function scope_end(opt, lineinfo)
local scope = #scopes
if scope > 1 then table.remove(scopes) end
local inside_local = false
for scope = scope-1, 1, -1 do
if scopes[scope].inside_local then inside_local = true; break end
end
f('EndScope', opt, lineinfo, inside_local)
end
local function parse_function_list(has_self, name, pos)
local c = lx:next(); assert(c[1] == '(')
f('Statement', c[1], c.lineinfo, true) -- generate Statement for function definition
scope_begin(c[1], c.lineinfo, true)
local vars = {} -- accumulate vars (if any) to send after 'Function'
if has_self then
local lineinfo = c.lineinfo+1 -- zero size
table.insert(vars, {'VarSelf', 'self', lineinfo, true})
end
while true do
local n = lx:peek()
if not (n.tag == 'Id' or n.tag == 'Keyword' and n[1] == '...') then break end
local c = lx:next()
if c.tag == 'Id' then table.insert(vars, {'Var', c[1], c.lineinfo, true}) end
-- ignore '...' in this case
if lx:peek()[1] == ',' then lx:next() end
end
if lx:peek()[1] == ')' then
lx:next()
f('Function', name, pos or c.lineinfo, true)
end
for _, var in ipairs(vars) do f(unpack(var)) end
end
while true do
local c = lx:next()
-- Detect end of previous statement
if c.tag == 'Eof' -- trigger 'Statement' at the end of file
or c.tag == 'Keyword' and (
c[1] == 'break' or c[1] == 'goto' or c[1] == 'do' or c[1] == 'while' or
c[1] == 'repeat' or c[1] == 'if' or c[1] == 'for' or c[1] == 'function' and lx:peek().tag == 'Id' or
c[1] == 'local' or c[1] == ';' or c[1] == 'until' or c[1] == 'return' or c[1] == 'end') or
c.tag == 'Id' and
(cprev.tag == 'Id' or
cprev.tag == 'Keyword' and
(cprev[1] == ']' or cprev[1] == ')' or cprev[1] == '}' or
cprev[1] == '...' or cprev[1] == 'end' or
cprev[1] == 'true' or cprev[1] == 'false' or
cprev[1] == 'nil') or
cprev.tag == 'Number' or cprev.tag == 'String')
then
if scopes[#scopes].inside_until then scope_end(nil, c.lineinfo) end
local scope = #scopes
if not scopes[scope].inside_table then scopes[scope].inside_local = nil end
f('Statement', c[1], c.lineinfo,
scopes[scope].inside_local or c[1] == 'local' or c[1] == 'function' or c[1] == 'end')
end
if c.tag == 'Eof' then break end
-- Process token(s)
if c.tag == 'Keyword' then
if c[1] == 'local' and lx:peek().tag == 'Keyword' and lx:peek()[1] == 'function' then
-- local function
local c = lx:next(); assert(c[1] == 'function')
if lx:peek().tag == 'Id' then
c = lx:next()
f('Var', c[1], c.lineinfo, true)
if lx:peek()[1] == '(' then parse_function_list(nil, c[1], c.lineinfo) end
end
elseif c[1] == 'function' then
if lx:peek()[1] == '(' then -- inline function
parse_function_list()
elseif lx:peek().tag == 'Id' then -- function definition statement
c = lx:next(); assert(c.tag == 'Id')
local name = c[1]
local pos = c.lineinfo
f('Id', name, pos, true)
local has_self
while lx:peek()[1] ~= '(' and lx:peek().tag ~= 'Eof' do
c = lx:next()
name = name .. c[1]
if c.tag == 'Id' then
f('String', c[1], c.lineinfo, true)
elseif c.tag == 'Keyword' and c[1] == ':' then
has_self = true
end
end
if lx:peek()[1] == '(' then parse_function_list(has_self, name, pos) end
end
elseif c[1] == 'local' and lx:peek().tag == 'Id' then
scopes[#scopes].inside_local = true
c = lx:next()
f('VarNext', c[1], c.lineinfo, true)
while lx:peek().tag == 'Keyword' and lx:peek()[1] == ',' do
c = lx:next(); if lx:peek().tag ~= 'Id' then break end
c = lx:next()
f('VarNext', c[1], c.lineinfo, true)
end
elseif c[1] == 'for' and lx:peek().tag == 'Id' then
c = lx:next()
f('VarInside', c[1], c.lineinfo, true)
while lx:peek().tag == 'Keyword' and lx:peek()[1] == ',' do
c = lx:next(); if lx:peek().tag ~= 'Id' then break end
c = lx:next()
f('VarInside', c[1], c.lineinfo, true)
end
elseif c[1] == 'goto' and lx:peek().tag == 'Id' then
lx:next()
elseif c[1] == 'do' then
scope_begin('do', c.lineinfo)
-- note: do/while/for statement scopes all begin at 'do'.
elseif c[1] == 'repeat' or c[1] == 'then' then
scope_begin(c[1], c.lineinfo)
elseif c[1] == 'end' or c[1] == 'elseif' then
scope_end(c[1], c.lineinfo)
elseif c[1] == 'else' then
scope_end(nil, c.lineinfo)
scope_begin(c[1], c.lineinfo)
elseif c[1] == 'until' then
scopes[#scopes].inside_until = true
elseif c[1] == '{' then
scopes[#scopes].inside_table = (scopes[#scopes].inside_table or 0) + 1
elseif c[1] == '}' then
local newval = (scopes[#scopes].inside_table or 0) - 1
newval = newval >= 1 and newval or nil
scopes[#scopes].inside_table = newval
end
elseif c.tag == 'Id' then
local scope = #scopes
local inside_local = scopes[scope].inside_local ~= nil
local inside_table = scopes[scope].inside_table
local cnext = lx:peek()
if cnext.tag == 'Keyword' and (cnext[1] == '(' or cnext[1] == '{')
or cnext.tag == 'String' then
f('FunctionCall', c[1], c.lineinfo, inside_local)
end
-- either this is inside a table or it continues from a comma,
-- which may be a field assignment, so assume it's in a table
if (inside_table or cprev[1] == ',') and cnext.tag == 'Keyword' and cnext[1] == '=' then
-- table field; table fields are tricky to handle during incremental
-- processing as "a = 1" may be either an assignment (in which case
-- 'a' is Id) or a field initialization (in which case it's a String).
-- Since it's not possible to decide between two cases in isolation,
-- this is not a good place to insert a break; instead, the break is
-- inserted at the location of the previous keyword, which allows
-- to properly handle those cases. The desired location of
-- the restart point is returned as the `nobreak` value.
f('String', c[1], c.lineinfo,
inside_local or cprev and cprev.tag == 'Keyword' and cprev.lineinfo)
elseif cprev.tag == 'Keyword' and (cprev[1] == ':' or cprev[1] == '.') then
f('String', c[1], c.lineinfo, true)
else
f('Id', c[1], c.lineinfo, true)
-- this looks like the left side of (multi-variable) assignment
-- unless it's a part of `= var, field = value`, so skip if inside a table
if not inside_table and not (cprev and cprev.tag == 'Keyword' and cprev[1] == '=') then
while lx:peek().tag == 'Keyword' and lx:peek()[1] == ',' do
local c = lx:next(); if lx:peek().tag ~= 'Id' then break end
c = lx:next()
f('Id', c[1], c.lineinfo, true)
end
end
end
end
if c.tag ~= 'Comment' then cprev = c end
end
end
--[[
This is similar to parse_scope but determines if variables are local or global.
lx - lexer stream of Lua tokens.
f(event...) - callback function to send events to.
Events generated:
'Id', name, lineinfo, 'local'|'global'
(plus all events in parse_scope)
--]]
function PARSE.parse_scope_resolve(lx, f, vars)
local NEXT = {} -- unique key
local INSIDE = {} -- unique key
local function newscope(vars, opt, lineinfo)
local newvars = opt=='do' and vars[INSIDE] or {}
if newvars == vars[INSIDE] then vars[INSIDE] = false end
newvars[INSIDE]=false
newvars[NEXT]=false
local level = (vars[0] or 0) + 1
newvars[0] = level -- keep the current level
newvars[-1] = lineinfo -- keep the start of the scope
newvars[level] = newvars -- reference the current vars table
return setmetatable(newvars, {__index=vars})
end
vars = vars or newscope({[0] = 0}, nil, 1)
vars[NEXT] = false -- vars that come into scope upon next statement
vars[INSIDE] = false -- vars that come into scope upon entering block
PARSE.parse_scope(lx, function(op, name, lineinfo, nobreak)
-- in some (rare) cases VarNext can follow Statement event (which copies
-- vars[NEXT]). This may cause vars[0] to be `nil`, so default to 1.
local var = op:find("^Var") and
{fpos = lineinfo, at = (vars[0] or 1) + (op == 'VarInside' and 1 or 0),
masked = vars[name], self = (op == 'VarSelf') or nil } or nil
if op == 'Var' or op == 'VarSelf' then
vars[name] = var
elseif op == 'VarNext' then
vars[NEXT] = vars[NEXT] or {}
vars[NEXT][name] = var
elseif op == 'VarInside' then
vars[INSIDE] = vars[INSIDE] or {}
vars[INSIDE][name] = var
elseif op == 'Scope' then
vars = newscope(vars, name, lineinfo)
elseif op == 'EndScope' then
local mt = getmetatable(vars)
if mt ~= nil then vars = mt.__index end
elseif op == 'Id'
or op == 'String' or op == 'FunctionCall' or op == 'Function' then
-- Just make callback
elseif op == 'Statement' then -- beginning of statement
-- Apply vars that come into scope upon beginning of statement.
if vars[NEXT] then
for k,v in pairs(vars[NEXT]) do
vars[k] = v; vars[NEXT][k] = nil
end
end
else
assert(false)
end
f(op, name, lineinfo, vars, nobreak)
end, vars[0])
end
function PARSE.extract_vars(code, f)
local lx = LEX.lexc(code)
local char0 = 1 -- next char offset to write
local function gen(char1, nextchar0)
char0 = nextchar0
end
PARSE.parse_scope_resolve(lx, function(op, name, lineinfo, other)
if op == 'Id' then
f('Id', name, other, lineinfo)
elseif op == 'Var' or op == 'VarNext' or op == 'VarInside' then
gen(lineinfo, lineinfo+#name)
f('Var', name, "local", lineinfo)
end -- ignore 'VarSelf' and others
end)
gen(#code+1, nil)
end
--[[
Converts 5.2 code to 5.1 style code with explicit _ENV variables.
Example: "function f(_ENV, x) print(x, y)" -->
"function _ENV.f(_ENV, x) _ENV.print(x, _ENV.y) end"
code - string of Lua code. Assumed to be valid Lua (FIX: 5.1 or 5.2?)
f(s) - call back function to send chunks of Lua code output to. Example: io.stdout.
--]]
function PARSE.replace_env(code, f)
if not f then return PARSE.accumulate(PARSE.replace_env, code) end
PARSE.extract_vars(code, function(op, name, other)
if op == 'Id' then
f(other == 'global' and '_ENV.' .. name or name)
elseif op == 'Var' or op == 'Other' then
f(name)
end
end)
end
-- helper function. Can be passed as argument `f` to functions
-- like `replace_env` above to accumulate fragments into a single string.
function PARSE.accumulator()
local ts = {}
local mt = {}
mt.__index = mt
function mt:__call(s) ts[#ts+1] = s end
function mt:result() return table.concat(ts) end
return setmetatable({}, mt)
end
-- helper function
function PARSE.accumulate(g, code)
local accum = PARSE.accumulator()
g(code, accum)
return accum:result()
end
return PARSE

View File

@ -1,746 +0,0 @@
#!/usr/bin/env lua
-- Command line interface to LuaDist-git.
local dist = require "dist"
local utils = require "dist.utils"
local depends = require "dist.depends"
local package = require "dist.package"
local mf = require "dist.manifest"
local cfg = require "dist.config"
local sys = require "dist.sys"
-- CLI commands of Luadist.
local commands
commands = {
-- Print help for this command line interface.
["help"] = {
help = [[
Usage: luadist [DEPLOYMENT_DIRECTORY] <COMMAND> [ARGUMENTS...] [-VARIABLES...]
Commands:
help - print this help
install - install modules
remove - remove modules
refresh - update information about modules in repositories
list - list installed modules
info - show information about modules
search - search repositories for modules
fetch - download modules
make - manually deploy modules from local paths
upload - upload installed modules to their repositories
tree - print dependency tree of a module
selftest - run the selftest of LuaDist
To get help on specific command, run:
luadist help <COMMAND>
]],
run = function (deploy_dir, help_item)
deploy_dir = deploy_dir or dist.get_deploy_dir()
help_item = help_item or {}
assert(type(deploy_dir) == "string", "luadist.help: Argument 'deploy_dir' is not a string.")
assert(type(help_item) == "table", "luadist.help: Argument 'help_item' is not a table.")
deploy_dir = sys.abs_path(deploy_dir)
if not help_item or not commands[help_item[1]] then
help_item = "help"
else
help_item = help_item[1]
end
print_info()
print(commands[help_item].help)
return 0
end
},
-- Install modules.
["install"] = {
help = [[
Usage: luadist [DEPLOYMENT_DIRECTORY] install MODULES... [-VARIABLES...]
The 'install' command will install specified MODULES to
DEPLOYMENT_DIRECTORY. LuaDist will also automatically resolve, download
and install all dependencies.
If DEPLOYMENT_DIRECTORY is not specified, the deployment directory
of LuaDist is used.
You can use * (an asterisk sign) in the name of the module as a wildcard
with the meaning 'any symbols' (in most shells, the module name then must
be quoted to prevent the expansion of asterisk by the shell itself).
Optional CMake VARIABLES in -D format (e.g. -Dvariable=value) or LuaDist
configuration VARIABLES (e.g. -variable=value) can be specified.
The -simulate configuration option makes LuaDist only to simulate the
installation of modules (no modules will be really installed).
]],
run = function (deploy_dir, modules, cmake_variables)
deploy_dir = deploy_dir or dist.get_deploy_dir()
if type(modules) == "string" then modules = {modules} end
cmake_variables = cmake_variables or {}
assert(type(deploy_dir) == "string", "luadist.install: Argument 'deploy_dir' is not a string.")
assert(type(modules) == "table", "luadist.install: Argument 'modules' is not a string or table.")
assert(type(cmake_variables) == "table", "luadist.install: Argument 'cmake_variables' is not a table.")
deploy_dir = sys.abs_path(deploy_dir)
if cfg.simulate then
print("NOTE: this is just simulation.")
end
if #modules == 0 then
print("No modules to install specified.")
return 0
end
local ok, err = dist.install(modules, deploy_dir, cmake_variables)
if not ok then
print(err)
os.exit(1)
else
print((cfg.simulate and "Simulated installation" or "Installation") .. " successful.")
return 0
end
end
},
-- Remove modules.
["remove"] = {
help = [[
Usage: luadist [DEPLOYMENT_DIRECTORY] remove MODULES... [-VARIABLES...]
The 'remove' command will remove specified MODULES from
DEPLOYMENT_DIRECTORY. If no module is specified, all modules
will be removed.
If DEPLOYMENT_DIRECTORY is not specified, the deployment directory
of LuaDist is used. If no MODULES are specified, all installed modules
will be removed.
You can use * (an asterisk sign) in the name of the module as a wildcard
with the meaning 'any symbols' (in most shells, the module name then must
be quoted to prevent the expansion of asterisk by the shell itself).
Optional LuaDist configuration VARIABLES (e.g. -variable=value) can be
specified.
WARNING: dependencies between modules are NOT taken into account when
removing modules!
]],
run = function (deploy_dir, modules)
deploy_dir = deploy_dir or dist.get_deploy_dir()
if type(modules) == "string" then modules = {modules} end
assert(type(deploy_dir) == "string", "luadist.remove: Argument 'deploy_dir' is not a string.")
assert(type(modules) == "table", "luadist.remove: Argument 'modules' is not a string or table.")
deploy_dir = sys.abs_path(deploy_dir)
local num, err = dist.remove(modules, deploy_dir)
if not num then
print(err)
os.exit(1)
else
print("Removed modules: " .. num)
return 0
end
end
},
-- Update repositories.
["refresh"] = {
help = [[
Usage: luadist [DEPLOYMENT_DIRECTORY] refresh [-VARIABLES...]
The 'refresh' command will update information about modules in all software
repositories of specified DEPLOYMENT_DIRECTORY. Also, the cached dependency
manifest, built from previous installations or invocations of 'tree'
functionality will be deleted.
If DEPLOYMENT_DIRECTORY is not specified, the deployment directory
of LuaDist is used.
Optional LuaDist configuration VARIABLES (e.g. -variable=value) can be
specified.
]],
run = function (deploy_dir)
deploy_dir = deploy_dir or dist.get_deploy_dir()
assert(type(deploy_dir) == "string", "luadist.refresh: Argument 'deploy_dir' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
-- TODO: should be deleting the dep_manifest decoupled from refreshing the repository info?
-- delete cached dependency manifest
local dep_manifest_file = sys.abs_path(sys.make_path(deploy_dir, cfg.dep_cache_file))
local dep_mf_deleted = false
if sys.exists(dep_manifest_file) then
sys.delete(dep_manifest_file)
dep_mf_deleted = true
end
-- refresh repository information
local ok, err = dist.update_manifest(deploy_dir)
if not ok then
print(err)
os.exit(1)
else
print("Repositories successfuly updated" .. (dep_mf_deleted and " and dependency cache deleted" or "") .. ".")
return 0
end
end
},
-- Manually deploy modules.
["make"] = {
help = [[
Usage: luadist [DEPLOYMENT_DIRECTORY] make MODULE_PATHS... [-VARIABLES...]
The 'make' command will manually deploy modules from specified local
MODULE_PATHS into the DEPLOYMENT_DIRECTORY.
The MODULE_PATHS will be preserved. If DEPLOYMENT_DIRECTORY is not
specified, the deployment directory of LuaDist is used.
Optional CMake VARIABLES in -D format (e.g. -Dvariable=value) or LuaDist
configuration VARIABLES (e.g. -variable=value) can be specified.
The -simulate configuration option makes LuaDist only to simulate the
deployment of modules (no modules will be really deployed).
WARNING: this command does NOT check whether the dependencies of deployed
modules are satisfied or not!
]],
run = function (deploy_dir, module_paths, cmake_variables)
deploy_dir = deploy_dir or dist.get_deploy_dir()
module_paths = module_paths or {}
cmake_variables = cmake_variables or {}
assert(type(deploy_dir) == "string", "luadist.make: Argument 'deploy_dir' is not a string.")
assert(type(module_paths) == "table", "luadist.make: Argument 'module_paths' is not a table.")
assert(type(cmake_variables) == "table", "luadist.make: Argument 'cmake_variables' is not a table.")
deploy_dir = sys.abs_path(deploy_dir)
if cfg.simulate then
print("NOTE: this is just simulation.")
end
if #module_paths == 0 then
print("No module paths to deploy specified.")
return 0
end
local ok, err = dist.make(deploy_dir, module_paths, cmake_variables)
if not ok then
print(err)
os.exit(1)
end
print((cfg.simulate and "Simulated deployment" or "Deployment") .. " successful.")
return 0
end
},
-- Download modules.
["fetch"] = {
help = [[
Usage: luadist [FETCH_DIRECTORY] fetch MODULES... [-VARIABLES...]
The 'fetch' command will download specified MODULES to the FETCH_DIRECTORY.
If no FETCH_DIRECTORY is specified, the temporary directory of LuaDist
deployment directory (i.e. ']] .. cfg.temp_dir .. [[') is used.
If the version is not specified in module name, the most recent version
available will be downloaded.
Optional LuaDist configuration VARIABLES (e.g. -variable=value) can be
specified.
]],
run = function (fetch_dir, modules)
fetch_dir = fetch_dir or dist.get_deploy_dir()
modules = modules or {}
assert(type(fetch_dir) == "string", "luadist.fetch: Argument 'fetch_dir' is not a string.")
assert(type(modules) == "table", "luadist.fetch: Argument 'modules' is not a table.")
fetch_dir = sys.abs_path(fetch_dir)
-- if the default parameter (i.e. deploy_dir) is passed, use the default temp_dir
if fetch_dir == dist.get_deploy_dir() then
fetch_dir = sys.make_path(fetch_dir, cfg.temp_dir)
end
if #modules == 0 then
print("No modules to download specified.")
return 0
end
local ok, err = dist.fetch(modules, fetch_dir)
if not ok then
print(err)
os.exit(1)
else
print("Modules successfuly downloaded to '" .. fetch_dir .. "'.")
return 0
end
end
},
-- Upload modules.
["upload"] = {
help = [[
Usage: luadist [DEPLOYMENT_DIRECTORY] upload MODULES... [-VARIABLES...]
The 'upload' command will upload the binary versions of specified MODULES,
installed in the DEPLOYMENT_DIRECTORY, to their LuaDist repositories.
Base url of repositories is given by configuration variable 'upload_url'
(by default ']] .. cfg.upload_url .. [[') which you can change.
E.g.: Binary version of module 'lua', installed in DEPLOYMENT_DIRECTORY,
will now be uploaded to repository ']] .. cfg.upload_url .. [[lua.git'.
Organization of uploaded modules and their repositories is subject
to the conventions described in more detail in the source code
of the 'dist.upload_modules()' function (file 'dist/init.lua').
If DEPLOYMENT_DIRECTORY is not specified, the deployment directory
of LuaDist is used. If no MODULES are specified, all installed modules
will be uploaded.
You can use * (an asterisk sign) in the name of the module as a wildcard
with the meaning 'any symbols' (in most shells, the module name then must
be quoted to prevent the expansion of asterisk by the shell itself).
Optional LuaDist configuration VARIABLES (e.g. -variable=value) can be
specified.
]],
run = function (deploy_dir, modules)
-- check if we have git
local ok = utils.system_dependency_available("git", "git --version")
if not ok then os.exit(1) end
deploy_dir = deploy_dir or dist.get_deploy_dir()
if type(modules) == "string" then modules = {modules} end
assert(type(deploy_dir) == "string", "luadist.upload: Argument 'deploy_dir' is not a string.")
assert(type(modules) == "table", "luadist.upload: Argument 'modules' is not a string or table.")
deploy_dir = sys.abs_path(deploy_dir)
local num, err = dist.upload_modules(deploy_dir, modules, cfg.upload_url)
if not num then
print(err)
os.exit(1)
else
print("Uploaded modules: " .. num)
return 0
end
end
},
-- List installed modules.
["list"] = {
help = [[
Usage: luadist [DEPLOYMENT_DIRECTORY] list [STRINGS...] [-VARIABLES...]
The 'list' command will list all modules installed in specified
DEPLOYMENT_DIRECTORY, which contain one or more optional STRINGS.
If DEPLOYMENT_DIRECTORY is not specified, the deployment directory
of LuaDist is used. If STRINGS are not specified, all installed modules
are listed.
Optional LuaDist configuration VARIABLES (e.g. -variable=value) can be
specified.
]],
run = function (deploy_dir, strings)
deploy_dir = deploy_dir or dist.get_deploy_dir()
strings = strings or {}
assert(type(deploy_dir) == "string", "luadist.list: Argument 'deploy_dir' is not a string.")
assert(type(strings) == "table", "luadist.list: Argument 'strings' is not a table.")
deploy_dir = sys.abs_path(deploy_dir)
local deployed = dist.get_deployed(deploy_dir)
deployed = depends.filter_packages_by_strings(deployed, strings)
print("\nInstalled modules:")
print("==================\n")
for _, pkg in pairs(deployed) do
print(" " .. pkg.name .. "-" .. pkg.version .. "\t(" .. pkg.arch .. "-" .. pkg.type .. ")" .. (pkg.provided_by and "\t [provided by " .. pkg.provided_by .. "]" or ""))
end
print()
return 0
end
},
-- Search for modules in repositories.
["search"] = {
help = [[
Usage: luadist [DEPLOYMENT_DIRECTORY] search [STRINGS...] [-VARIABLES...]
The 'search' command will list all modules from repositories, which contain
one or more STRINGS.
If no STRINGS are specified, all available modules are listed.
Optional LuaDist configuration VARIABLES (e.g. -variable=value) can be
specified.
]],
run = function (deploy_dir, strings)
deploy_dir = deploy_dir or dist.get_deploy_dir()
strings = strings or {}
assert(type(deploy_dir) == "string", "luadist.search: Argument 'deploy_dir' is not a string.")
assert(type(strings) == "table", "luadist.search: Argument 'strings' is not a table.")
deploy_dir = sys.abs_path(deploy_dir)
local available, err = mf.get_manifest()
if not available then
print(err)
os.exit(1)
end
available = depends.filter_packages_by_strings(available, strings)
available = depends.sort_by_names(available)
print("\nModules found:")
print("==============\n")
for _, pkg in pairs(available) do
print(" " .. pkg.name)
end
print()
return 0
end
},
-- Show information about modules.
["info"] = {
help = [[
Usage: luadist [DEPLOYMENT_DIRECTORY] info [MODULES...] [-VARIABLES...]
The 'info' command shows information about specified modules from
repositories. This command also shows whether modules are installed
in DEPLOYMENT_DIRECTORY.
If no MODULES are specified, all available modules are shown.
If DEPLOYMENT_DIRECTORY is not specified, the deployment directory
of LuaDist is used.
Optional LuaDist configuration VARIABLES (e.g. -variable=value) can be
specified.
]],
run = function (deploy_dir, modules)
deploy_dir = deploy_dir or dist.get_deploy_dir()
modules = modules or {}
assert(type(deploy_dir) == "string", "luadist.info: Argument 'deploy_dir' is not a string.")
assert(type(modules) == "table", "luadist.info: Argument 'modules' is not a table.")
deploy_dir = sys.abs_path(deploy_dir)
local manifest, err = mf.get_manifest()
if not manifest then
print(err)
os.exit(1)
end
-- if no packages specified explicitly, show just info from .gitmodules for all packages available
if #modules == 0 then
modules = manifest
modules = depends.sort_by_names(modules)
local deployed = dist.get_deployed(deploy_dir)
print("")
for _, pkg in pairs(modules) do
print(" " .. pkg.name)
print(" Repository url: " .. (pkg.path or "N/A"))
print()
end
return 0
-- if some packages explicitly specified, retrieve and show detailed info about them
else
if #modules > 5 then
print("NOTE: More than 5 modules specified - operation may take a longer time.")
end
local deployed = dist.get_deployed(deploy_dir)
for _, module in pairs(modules) do
manifest, err = package.get_versions_info(module, manifest, deploy_dir, deployed)
if not manifest then
print(err)
os.exit(1)
end
end
modules = depends.find_packages(modules, manifest)
modules = depends.sort_by_names(modules)
print("")
for _, pkg in pairs(modules) do
print(" " .. pkg.name .. "-" .. pkg.version .. " (" .. pkg.arch .. "-" .. pkg.type ..")" .. (pkg.from_installed and " [info taken from installed version]" or ""))
print(" Description: " .. (pkg.desc or "N/A"))
print(" Author: " .. (pkg.author or "N/A"))
print(" Homepage: " .. (pkg.url or "N/A"))
print(" License: " .. (pkg.license or "N/A"))
print(" Repository url: " .. (pkg.path or "N/A"))
print(" Maintainer: " .. (pkg.maintainer or "N/A"))
if pkg.provides then print(" Provides: " .. utils.table_tostring(pkg.provides)) end
if pkg.depends then print(" Depends: " .. utils.table_tostring(pkg.depends)) end
if pkg.conflicts then print(" Conflicts: " .. utils.table_tostring(pkg.conflicts)) end
print(" State: " .. (depends.is_installed(pkg.name, deployed, pkg.version) and "installed" or "not installed"))
print()
end
return 0
end
end
},
-- Print dependency tree.
["tree"] = {
help = [[
Usage: luadist [DEPLOYMENT_DIRECTORY] tree [MODULES...] [-VARIABLES...]
The 'tree' command prints dependency tree for specified modules.
If no MODULES are specified, trees for all available modules are printed.
This information about modules is being cached in dependency manifest.
Optional LuaDist configuration VARIABLES (e.g. -variable=value) can be
specified.
]],
run = function (deploy_dir, modules)
deploy_dir = deploy_dir or dist.get_deploy_dir()
modules = modules or {}
assert(type(deploy_dir) == "string", "luadist.info: Argument 'deploy_dir' is not a string.")
assert(type(modules) == "table", "luadist.info: Argument 'modules' is not a table.")
deploy_dir = sys.abs_path(deploy_dir)
local manifest, err = mf.get_manifest()
if not manifest then
print(err)
os.exit(1)
end
-- if no modules specified explicitly, assume all modules
if #modules == 0 then modules = depends.sort_by_names(manifest) end
print("Getting dependency information... (this may take a lot of time)")
for _, module in pairs(modules) do
-- if all modules are being queried, extract the name
if type(module) == "table" then module = module.name end
local dep_manifest, err = dist.dependency_info(module, deploy_dir)
if not dep_manifest then
print(err)
os.exit(1)
else
-- print the dependency tree
local heading = "Dependency tree for '" .. module .. "' (on " .. cfg.arch .. "-" .. cfg.type .. "):"
print("\n" .. heading .. "")
print(string.rep("=", #heading) .. "\n")
for _, pkg in pairs(dep_manifest) do
local pkg_version, pkg_tag = pkg.version, pkg.version
if pkg.was_scm_version then
pkg_version, pkg_tag = "scm", "HEAD"
end
print(" " .. pkg.name .. "-" .. pkg_version .. " (" .. pkg.path .. ", " .. pkg_tag .. ")")
if pkg.depends then
for _, dep in pairs(pkg.depends) do
if type(dep) ~= "table" then
local found = depends.sort_by_versions(depends.find_packages(dep, dep_manifest))[1]
if not found then
print("Could not find the dependency '" .. dep .. "' in the dependency manifest.")
os.exit(1)
end
print(" * " .. found.name .. "-" .. found.version .. " (" .. found.path .. ", " .. found.version .. ")")
end
end
end
print()
end
end
end
return 0
end
},
-- Selftest of LuaDist.
["selftest"] = {
help = [[
Usage: luadist [TEST_DIRECTORY] selftest [-VARIABLES...]
The 'selftest' command runs tests of LuaDist, located in TEST_DIRECTORY and
displays the results.
If no TEST_DIRECTORY is specified, the default test directory of LuaDist
deployment directory (i.e. ']] .. cfg.test_dir .. [[') is used.
Optional LuaDist configuration VARIABLES (e.g. -variable=value) can be
specified.
]],
run = function (test_dir)
test_dir = test_dir or dist.get_deploy_dir()
assert(type(test_dir) == "string", "luadist.selftest: Argument 'deploy_dir' is not a string.")
test_dir = sys.abs_path(test_dir)
-- if the default parameter (i.e. deploy_dir) is passed, use the default test_dir
if test_dir == dist.get_deploy_dir() then
test_dir = sys.make_path(test_dir, cfg.test_dir)
end
-- try to get an iterator over test files and check it
local test_iterator, err = sys.get_directory(test_dir)
if not test_iterator then
print("Running tests from '" .. test_dir .. "' failed: " .. err)
os.exit(1)
end
-- run the tests
print("\nRunning tests:")
print("==============")
for test_file in sys.get_directory(test_dir) do
test_file = sys.make_path(test_dir, test_file)
if sys.is_file(test_file) then
print()
print(sys.extract_name(test_file) .. ":")
dofile(test_file)
end
end
print()
return 0
end
},
}
-- Run the functionality of LuaDist 'command' in the 'deploy_dir' with other items
-- or settings/variables starting at 'other_idx' index of special variable 'arg'.
local function run_command(deploy_dir, command, other_idx)
deploy_dir = deploy_dir or dist.get_deploy_dir()
assert(type(deploy_dir) == "string", "luadist.run_command: Argument 'deploy_dir' is not a string.")
assert(type(command) == "string", "luadist.run_command: Argument 'command' is not a string.")
assert(not other_idx or type(other_idx) == "number", "luadist.run_command: Argument 'other_idx' is not a number.")
deploy_dir = sys.abs_path(deploy_dir)
local items = {}
local cmake_variables = {}
-- parse items after the command (and LuaDist or CMake variables)
if other_idx then
for i = other_idx, #arg do
-- CMake variable
if arg[i]:match("^%-D(.-)=(.*)$") then
local variable, value = arg[i]:match("^%-D(.-)=(.*)$")
cmake_variables[variable] = value
-- LuaDist variable
elseif arg[i]:match("^%-(.-)=(.*)$") then
local variable, value = arg[i]:match("^%-(.-)=(.*)$")
apply_settings(variable, value)
-- LuaDist boolean variable with implicit 'true' value
elseif arg[i]:match("^%-(.-)$") then
local variable, value = arg[i]:match("^%-(.-)$")
apply_settings(variable, "true")
-- not a LuaDist or CMake variable
else
table.insert(items, arg[i])
end
end
end
-- run the required LuaDist functionality
return commands[command].run(sys.abs_path(deploy_dir), items, cmake_variables)
end
-- Print information about Luadist (version, license, etc.).
function print_info()
print([[
LuaDist-git ]].. cfg.version .. [[ - Lua package manager for the LuaDist deployment system.
Released under the MIT License. See https://github.com/luadist/luadist-git
]])
return 0
end
-- Convenience function for printing the main luadist help.
function print_help()
return run_command(nil, "help")
end
-- Set the LuaDist 'variable' to the 'value'.
-- See available settings in 'dist.config' module.
function apply_settings(variable, value)
assert(type(variable) == "string", "luadist.apply_settings: Argument 'variable' is not a string.")
assert(type(value) == "string", "luadist.apply_settings: Argument 'value' is not a string.")
-- check whether the settings variable exists
if cfg[variable] == nil then
print("Unknown LuaDist configuration option: '" .. variable .. "'.")
os.exit(1)
-- ensure the right type
elseif type(cfg[variable]) == "boolean" then
value = value:lower()
if value == "true" or value == "yes" or value == "on" or value == "1" then
value = true
elseif value == "false" or value == "no" or value == "off" or value == "0" then
value = false
else
print("Value of LuaDist option '" .. variable .. "' must be a boolean.")
os.exit(1)
end
elseif type(cfg[variable]) == "number" then
value = tonumber(value)
if not value then
print("Value of LuaDist option '" .. variable .. "' must be a number.")
os.exit(1)
end
elseif type(cfg[variable]) == "table" then
local err
value, err = utils.make_table(value, ",")
if not value then
print("Error when parsing the LuaDist variable '" .. variable .. "': " .. err)
os.exit(1)
end
end
-- set the LuaDist variable
cfg[variable] = value
end
-- Parse command line input and run the required command.
if pcall(debug.getlocal, 4, 1) then
return commands -- return commands when used as module
elseif not commands[arg[1]] and commands[arg[2]] then
-- deploy_dir specified
return run_command(arg[1], arg[2], 3)
elseif commands[arg[1]] then
-- deploy_dir not specified
return run_command(dist.get_deploy_dir(), arg[1], 2)
else
-- unknown command
if arg[1] then
print("Unknown command '" .. arg[1] .. "'. Printing help...\n")
print_help()
os.exit(1)
end
return print_help()
end

View File

@ -1,915 +0,0 @@
-- luainspect.ast - Lua Abstract Syntax Tree (AST) and token list operations.
--
-- Two main structures are maintained. A Metalua-style AST represents the
-- nested syntactic structure obtained from the parse.
-- A separate linear ordered list of tokens represents the syntactic structure
-- from the lexing, including line information (character positions only not row/columns),
-- comments, and keywords, which is originally built from the lineinfo attributes
-- injected by Metalua into the AST (IMPROVE: it probably would be simpler
-- to obtain this from the lexer directly rather then inferring it from the parsing).
-- During AST manipulations, the lineinfo maintained in the AST is ignored
-- because it was found more difficult to maintain and not in the optimal format.
--
-- The contained code deals with
-- - Building the AST from source.
-- - Building the tokenlist from the AST lineinfo.
-- - Querying the AST+tokenlist.
-- - Modifying the AST+tokenlist (including incremental parsing source -> AST)
-- - Annotating the AST with navigational info (e.g. parent links) to assist queries.
-- - Dumping the tokenlist for debugging.
--
-- (c) 2010 David Manura, MIT License.
--! require 'luainspect.typecheck' (context)
local mlc = require 'metalua.compiler'.new()
local M = {}
--[=TESTSUITE
-- utilities
local ops = {}
ops['=='] = function(a,b) return a == b end
local function check(opname, a, b)
local op = assert(ops[opname])
if not op(a,b) then
error("fail == " .. tostring(a) .. " " .. tostring(b))
end
end
--]=]
-- CATEGORY: debug
local function DEBUG(...)
if LUAINSPECT_DEBUG then
print('DEBUG:', ...)
end
end
-- Converts character position to row,column position in string src.
-- Add values are 1-indexed.
function M.pos_to_linecol(pos, src)
local linenum = 1
local lasteolpos = 0
for eolpos in src:gmatch"()\n" do
if eolpos > pos then break end
linenum = linenum + 1
lasteolpos = eolpos
end
local colnum = pos - lasteolpos
return linenum, colnum
end
-- Removes any sheband ("#!") line from Lua source string.
-- CATEGORY: Lua parsing
function M.remove_shebang(src)
local shebang = src:match("^#![^\r\n]*")
return shebang and (" "):rep(#shebang) .. src:sub(#shebang+1) or src
end
-- Custom version of loadstring that parses out line number info
-- CATEGORY: Lua parsing
function M.loadstring(src)
local f, err = loadstring(src, "")
if f then
return f
else
err = err:gsub('^%[string ""%]:', "")
local linenum = assert(err:match("(%d+):"))
local colnum = 0
local linenum2 = err:match("^%d+: '[^']+' expected %(to close '[^']+' at line (%d+)")
return nil, err, linenum, colnum, linenum2
end
end
-- helper for ast_from_string. Raises on error.
-- FIX? filename currently ignored in Metalua
-- CATEGORY: Lua parsing
local function ast_from_string_helper(src, filename)
return mlc:src_to_ast(src, filename)
end
-- Counts number of lines in text.
-- Warning: the decision of whether to count a trailing new-line in a file
-- or an empty file as a line is a little subjective. This function currently
-- defines the line count as 1 plus the number of new line characters.
-- CATEGORY: utility/string
local function linecount(text)
local n = 1
for _ in text:gmatch'\n' do
n = n + 1
end
return n
end
-- Converts Lua source string to Lua AST (via mlp/gg).
-- CATEGORY: Lua parsing
function M.ast_from_string(src, filename)
local ok, ast = pcall(ast_from_string_helper, src, filename)
if not ok then
local err = ast
err = err:match('[^\n]*')
err = err:gsub("^.-:%s*line", "line")
-- mlp.chunk prepending this is undesirable. error(msg,0) would be better in gg.lua. Reported.
-- TODO-Metalua: remove when fixed in Metalua.
local linenum, colnum = err:match("line (%d+), char (%d+)")
if not linenum then
-- Metalua libraries may return "...gg.lua:56: .../mlp_misc.lua:179: End-of-file expected"
-- without the normal line/char numbers given things like "if x then end end". Should be
-- fixed probably with gg.parse_error in _chunk in mlp_misc.lua.
-- TODO-Metalua: remove when fixed in Metalua.
linenum = linecount(src)
colnum = 1
end
local linenum2 = nil
return nil, err, linenum, colnum, linenum2
else
return ast
end
end
-- Simple comment parser. Returns Metalua-style comment.
-- CATEGORY: Lua lexing
local function quick_parse_comment(src)
local s = src:match"^%-%-([^\n]*)()\n$"
if s then return {s, 1, #src, 'short'} end
local _, s = src:match(lexer.lexer.patterns.long_comment .. '\r?\n?$')
if s then return {s, 1, #src, 'long'} end
return nil
end
--FIX:check new-line correctness
--note: currently requiring \n at end of single line comment to avoid
-- incremental compilation with `--x\nf()` and removing \n from still
-- recognizing as comment `--x`.
-- currently allowing \r\n at end of long comment since Metalua includes
-- it in lineinfo of long comment (FIX:Metalua?)
-- Gets length of longest prefix string in both provided strings.
-- Returns max n such that text1:sub(1,n) == text2:sub(1,n) and n <= max(#text1,#text2)
-- CATEGORY: string utility
local function longest_prefix(text1, text2)
local nmin = 0
local nmax = math.min(#text1, #text2)
while nmax > nmin do
local nmid = math.ceil((nmin+nmax)/2)
if text1:sub(1,nmid) == text2:sub(1,nmid) then
nmin = nmid
else
nmax = nmid-1
end
end
return nmin
end
-- Gets length of longest postfix string in both provided strings.
-- Returns max n such that text1:sub(-n) == text2:sub(-n) and n <= max(#text1,#text2)
-- CATEGORY: string utility
local function longest_postfix(text1, text2)
local nmin = 0
local nmax = math.min(#text1, #text2)
while nmax > nmin do
local nmid = math.ceil((nmin+nmax)/2)
if text1:sub(-nmid) == text2:sub(-nmid) then --[*]
nmin = nmid
else
nmax = nmid-1
end
end
return nmin
end -- differs from longest_prefix only on line [*]
-- Determines AST node that must be re-evaluated upon changing code string from
-- `src` to `bsrc`, given previous top_ast/tokenlist/src.
-- Note: decorates top_ast as side-effect.
-- If preserve is true, then does not expand AST match even if replacement is invalid.
-- CATEGORY: AST/tokenlist manipulation
function M.invalidated_code(top_ast, tokenlist, src, bsrc, preserve)
-- Converts posiiton range in src to position range in bsrc.
local function range_transform(src_fpos, src_lpos)
local src_nlpos = #src - src_lpos
local bsrc_fpos = src_fpos
local bsrc_lpos = #bsrc - src_nlpos
return bsrc_fpos, bsrc_lpos
end
if src == bsrc then return end -- up-to-date
-- Find range of positions in src that differences correspond to.
-- Note: for zero byte range, src_pos2 = src_pos1 - 1.
local npre = longest_prefix(src, bsrc)
local npost = math.min(#src-npre, longest_postfix(src, bsrc))
-- note: min avoids overlap ambiguity
local src_fpos, src_lpos = 1 + npre, #src - npost
-- Find smallest AST node containing src range above. May also
-- be contained in (smaller) comment or whitespace.
local match_ast, match_comment, iswhitespace =
M.smallest_ast_containing_range(top_ast, tokenlist, src_fpos, src_lpos)
DEBUG('invalidate-smallest:', match_ast and (match_ast.tag or 'notag'), match_comment, iswhitespace)
-- Determine which (ast, comment, or whitespace) to match, and get its pos range in src and bsrc.
local srcm_fpos, srcm_lpos, bsrcm_fpos, bsrcm_lpos, mast, mtype
if iswhitespace then
mast, mtype = nil, 'whitespace'
srcm_fpos, srcm_lpos = src_fpos, src_lpos
elseif match_comment then
mast, mtype = match_comment, 'comment'
srcm_fpos, srcm_lpos = match_comment.fpos, match_comment.lpos
else
mast, mtype = match_ast, 'ast'
repeat
srcm_fpos, srcm_lpos = M.ast_pos_range(mast, tokenlist)
if not srcm_fpos then
if mast == top_ast then
srcm_fpos, srcm_lpos = 1, #src
break
else
M.ensure_parents_marked(top_ast)
mast = mast.parent
end
end
until srcm_fpos
end
bsrcm_fpos, bsrcm_lpos = range_transform(srcm_fpos, srcm_lpos)
-- Never expand match if preserve specified.
if preserve then
return srcm_fpos, srcm_lpos, bsrcm_fpos, bsrcm_lpos, mast, mtype
end
-- Determine if replacement could break parent nodes.
local isreplacesafe
if mtype == 'whitespace' then
if bsrc:sub(bsrcm_fpos, bsrcm_lpos):match'^%s*$' then -- replaced with whitespace
if bsrc:sub(bsrcm_fpos-1, bsrcm_lpos+1):match'%s' then -- not eliminating whitespace
isreplacesafe = true
end
end
elseif mtype == 'comment' then
local m2src = bsrc:sub(bsrcm_fpos, bsrcm_lpos)
DEBUG('invalidate-comment[' .. m2src .. ']')
if quick_parse_comment(m2src) then -- replaced with comment
isreplacesafe = true
end
end
if isreplacesafe then -- return on safe replacement
return srcm_fpos, srcm_lpos, bsrcm_fpos, bsrcm_lpos, mast, mtype
end
-- Find smallest containing statement block that will compile (or top_ast).
while 1 do
match_ast = M.get_containing_statementblock(match_ast, top_ast)
if match_ast == top_ast then
return 1,#src, 1, #bsrc, match_ast, 'statblock'
-- entire AST invalidated
end
local srcm_fpos, srcm_lpos = M.ast_pos_range(match_ast, tokenlist)
local bsrcm_fpos, bsrcm_lpos = range_transform(srcm_fpos, srcm_lpos)
local msrc = bsrc:sub(bsrcm_fpos, bsrcm_lpos)
DEBUG('invalidate-statblock:', match_ast and match_ast.tag, '[' .. msrc .. ']')
if loadstring(msrc) then -- compiled
return srcm_fpos, srcm_lpos, bsrcm_fpos, bsrcm_lpos, match_ast, 'statblock'
end
M.ensure_parents_marked(top_ast)
match_ast = match_ast.parent
end
end
-- Walks AST `ast` in arbitrary order, visiting each node `n`, executing `fdown(n)` (if specified)
-- when doing down and `fup(n)` (if specified) when going if.
-- CATEGORY: AST walk
function M.walk(ast, fdown, fup)
assert(type(ast) == 'table')
if fdown then fdown(ast) end
for _,bast in ipairs(ast) do
if type(bast) == 'table' then
M.walk(bast, fdown, fup)
end
end
if fup then fup(ast) end
end
-- Replaces contents of table t1 with contents of table t2.
-- Does not change metatable (if any).
-- This function is useful for swapping one AST node with another
-- while preserving any references to the node.
-- CATEGORY: table utility
function M.switchtable(t1, t2)
for k in pairs(t1) do t1[k] = nil end
for k in pairs(t2) do t1[k] = t2[k] end
end
-- Inserts all elements in list bt at index i in list t.
-- CATEGORY: table utility
local function tinsertlist(t, i, bt)
local oldtlen, delta = #t, i - 1
for ti = #t + 1, #t + #bt do t[ti] = false end -- preallocate (avoid holes)
for ti = oldtlen, i, -1 do t[ti + #bt] = t[ti] end -- shift
for bi = 1, #bt do t[bi + delta] = bt[bi] end -- fill
end
--[=[TESTSUITE:
local function _tinsertlist(t, i, bt)
for bi=#bt,1,-1 do table.insert(t, i, bt[bi]) end
end -- equivalent but MUCH less efficient for large tables
local function _tinsertlist(t, i, bt)
for bi=1,#bt do table.insert(t, i+bi-1, bt[bi]) end
end -- equivalent but MUCH less efficient for large tables
local t = {}; tinsertlist(t, 1, {}); assert(table.concat(t)=='')
local t = {}; tinsertlist(t, 1, {2,3}); assert(table.concat(t)=='23')
local t = {4}; tinsertlist(t, 1, {2,3}); assert(table.concat(t)=='234')
local t = {2}; tinsertlist(t, 2, {3,4}); assert(table.concat(t)=='234')
local t = {4,5}; tinsertlist(t, 1, {2,3}); assert(table.concat(t)=='2345')
local t = {2,5}; tinsertlist(t, 2, {3,4}); assert(table.concat(t)=='2345')
local t = {2,3}; tinsertlist(t, 3, {4,5}); assert(table.concat(t)=='2345')
print 'DONE'
--]=]
-- Gets list of keyword positions related to node ast in source src
-- note: ast must be visible, i.e. have lineinfo (e.g. unlike `Id "self" definition).
-- Note: includes operators.
-- Note: Assumes ast Metalua-style lineinfo is valid.
-- CATEGORY: tokenlist build
function M.get_keywords(ast, src)
local list = {}
if not ast.lineinfo then return list end
-- examine space between each pair of children i and j.
-- special cases: 0 is before first child and #ast+1 is after last child
-- Put children in lexical order.
-- Some binary operations have arguments reversed from lexical order.
-- For example, `a > b` becomes `Op{'lt', `Id 'b', `Id 'a'}
local oast =
(ast.tag == 'Op' and #ast == 3 and tostring(ast[2].lineinfo.first):match('|L(%d+)') > tostring(ast[3].lineinfo.first):match('|L(%d+)'))
and {ast[1], ast[3], ast[2]} or ast
local i = 0
while i <= #ast do
-- j is node following i that has lineinfo
local j = i+1; while j < #ast+1 and not oast[j].lineinfo do j=j+1 end
-- Get position range [fpos,lpos] between subsequent children.
local fpos
if i == 0 then -- before first child
fpos = tonumber(tostring(ast.lineinfo.first):match('|L(%d+)'))
else
local last = oast[i].lineinfo.last; local c = last.comments
fpos = (c and #c > 0 and c[#c][3] or tostring(last):match('|L(%d+)')) + 1
end
local lpos
if j == #ast+1 then -- after last child
lpos = tonumber(tostring(ast.lineinfo.last):match('|L(%d+)'))
else
local first = oast[j].lineinfo.first; local c = first.comments
lpos = (c and #c > 0 and c[1][2] or tostring(first):match('|L(%d+)')) - 1
end
-- Find keyword in range.
local spos = fpos
repeat
local mfpos, tok, mlppos = src:match("^%s*()(%a+)()", spos)
if not mfpos then
mfpos, tok, mlppos = src:match("^%s*()(%p+)()", spos)
end
if mfpos then
local mlpos = mlppos-1
if mlpos > lpos then mlpos = lpos end
if mlpos >= mfpos then
list[#list+1] = mfpos
list[#list+1] = mlpos
end
end
spos = mlppos
until not spos or spos > lpos
-- note: finds single keyword. in `local function` returns only `local`
--DEBUG(i,j ,'test[' .. src:sub(fpos, lpos) .. ']')
i = j -- next
--DESIGN:Lua: comment: string.match accepts a start position but not a stop position
end
return list
end
-- Q:Metalua: does ast.lineinfo[loc].comments imply #ast.lineinfo[loc].comments > 0 ?
-- Generates ordered list of tokens in top_ast/src.
-- Note: currently ignores operators and parens.
-- Note: Modifies ast.
-- Note: Assumes ast Metalua-style lineinfo is valid.
-- CATEGORY: AST/tokenlist query
local isterminal = {Nil=true, Dots=true, True=true, False=true, Number=true, String=true,
Dots=true, Id=true}
local function compare_tokens_(atoken, btoken) return atoken.fpos < btoken.fpos end
function M.ast_to_tokenlist(top_ast, src)
local tokens = {} -- {nbytes=#src}
local isseen = {}
M.walk(top_ast, function(ast)
if isterminal[ast.tag] then -- Extract terminal
local token = ast
if ast.lineinfo then
token.fpos = tonumber(tostring(ast.lineinfo.first):match('|L(%d+)'))
token.lpos = tonumber(tostring(ast.lineinfo.last):match('|L(%d+)'))
token.ast = ast
table.insert(tokens, token)
end
else -- Extract non-terminal
local keywordposlist = M.get_keywords(ast, src)
for i=1,#keywordposlist,2 do
local fpos, lpos = keywordposlist[i], keywordposlist[i+1]
local toksrc = src:sub(fpos, lpos)
local token = {tag='Keyword', fpos=fpos, lpos=lpos, ast=ast, toksrc}
table.insert(tokens, token)
end
end
-- Extract comments
for i=1,2 do
local comments = ast.lineinfo and ast.lineinfo[i==1 and 'first' or 'last'].comments
if comments then for _, comment in ipairs(comments) do
if not isseen[comment] then
comment.tag = 'Comment'
local token = comment
token.fpos = tonumber(tostring(comment.lineinfo.first):match('|L(%d+)'))
token.lpos = tonumber(tostring(comment.lineinfo.last):match('|L(%d+)'))
token.ast = comment
table.insert(tokens, token)
isseen[comment] = true
end
end end
end
end, nil)
table.sort(tokens, compare_tokens_)
return tokens
end
-- Gets tokenlist range [fidx,lidx] covered by ast. Returns nil,nil if not found.
--FIX:PERFORMANCE:this is slow on large files.
-- CATEGORY: AST/tokenlist query
function M.ast_idx_range_in_tokenlist(tokenlist, ast)
-- Get list of primary nodes under ast.
local isold = {}; M.walk(ast, function(ast) isold[ast] = true end)
-- Get range.
local fidx, lidx
for idx=1,#tokenlist do
local token = tokenlist[idx]
if isold[token.ast] then
lidx = idx
if not fidx then fidx = idx end
end
end
return fidx, lidx
end
-- Gets index range in tokenlist overlapped by character position range [fpos, lpos].
-- For example, `do ff() end` with range ` ff() ` would match tokens `ff()`.
-- Tokens partly inside range are counted, so range `f()` would match tokens `ff()`.
-- If lidx = fidx - 1, then position range is whitespace between tokens lidx (on left)
-- and fidx (on right), and this may include token pseudoindices 0 (start of file) and
-- #tokenlist+1 (end of file).
-- Note: lpos == fpos - 1 indicates zero-width range between chars lpos and fpos.
-- CATEGORY: tokenlist query
function M.tokenlist_idx_range_over_pos_range(tokenlist, fpos, lpos)
-- Find first/last indices of tokens overlapped (even partly) by position range.
local fidx, lidx
for idx=1,#tokenlist do
local token = tokenlist[idx]
--if (token.fpos >= fpos and token.fpos <= lpos) or (token.lpos >= fpos and token.lpos <= lpos) then -- token overlaps range
if fpos <= token.lpos and lpos >= token.fpos then -- range overlaps token (even partially)
if not fidx then fidx = idx end
lidx = idx
end
end
if not fidx then -- on fail, check between tokens
for idx=1,#tokenlist+1 do -- between idx-1 and idx
local tokfpos, toklpos = tokenlist[idx-1] and tokenlist[idx-1].lpos, tokenlist[idx] and tokenlist[idx].fpos
if (not tokfpos or fpos > tokfpos) and (not toklpos or lpos < toklpos) then -- range between tokens
return idx, idx-1
end
end
end
return fidx, lidx
end
--[=[TESTSUITE
local function test(...)
return table.concat({M.tokenlist_idx_range_over_pos_range(...)}, ',')
end
check('==', test({}, 2, 2), "1,0") -- no tokens
check('==', test({{tag='Id', fpos=1, lpos=1}}, 2, 2), "2,1") -- right of one token
check('==', test({{tag='Id', fpos=3, lpos=3}}, 2, 2), "1,0") -- left of one token
check('==', test({{tag='Id', fpos=3, lpos=4}}, 2, 3), "1,1") -- left partial overlap one token
check('==', test({{tag='Id', fpos=3, lpos=4}}, 4, 5), "1,1") -- right partial overlap one token
check('==', test({{tag='Id', fpos=3, lpos=6}}, 4, 5), "1,1") -- partial inner overlap one token
check('==', test({{tag='Id', fpos=3, lpos=6}}, 3, 6), "1,1") -- exact overlap one token
check('==', test({{tag='Id', fpos=4, lpos=5}}, 3, 6), "1,1") -- extra overlap one token
check('==', test({{tag='Id', fpos=2, lpos=3}, {tag='Id', fpos=5, lpos=6}}, 4, 4), "2,1") -- between tokens, " " exact
check('==', test({{tag='Id', fpos=2, lpos=3}, {tag='Id', fpos=5, lpos=6}}, 4, 3), "2,1") -- between tokens, "" on left
check('==', test({{tag='Id', fpos=2, lpos=3}, {tag='Id', fpos=5, lpos=6}}, 5, 4), "2,1") -- between tokens, "" on right
check('==', test({{tag='Id', fpos=2, lpos=3}, {tag='Id', fpos=4, lpos=5}}, 4, 3), "2,1") -- between tokens, "" exact
--]=]
-- Removes tokens in tokenlist covered by ast.
-- CATEGORY: tokenlist manipulation
local function remove_ast_in_tokenlist(tokenlist, ast)
local fidx, lidx = M.ast_idx_range_in_tokenlist(tokenlist, ast)
if fidx then -- note: fidx implies lidx
for idx=lidx,fidx,-1 do table.remove(tokenlist, idx) end
end
end
-- Inserts tokens from btokenlist into tokenlist. Preserves sort.
-- CATEGORY: tokenlist manipulation
local function insert_tokenlist(tokenlist, btokenlist)
local ftoken = btokenlist[1]
if ftoken then
-- Get index in tokenlist in which to insert tokens in btokenlist.
local fidx
for idx=1,#tokenlist do
if tokenlist[idx].fpos > ftoken.fpos then fidx = idx; break end
end
fidx = fidx or #tokenlist + 1 -- else append
-- Insert tokens.
tinsertlist(tokenlist, fidx, btokenlist)
end
end
-- Get character position range covered by ast in tokenlist. Returns nil,nil on not found.
-- CATEGORY: AST/tokenlist query
function M.ast_pos_range(ast, tokenlist) -- IMPROVE:style: ast_idx_range_in_tokenlist has params reversed
local fidx, lidx = M.ast_idx_range_in_tokenlist(tokenlist, ast)
if fidx then
return tokenlist[fidx].fpos, tokenlist[lidx].lpos
else
return nil, nil
end
end
-- Gets string representation of AST node. nil if none.
-- IMPROVE: what if node is empty block?
-- CATEGORY: AST/tokenlist query
function M.ast_to_text(ast, tokenlist, src) -- IMPROVE:style: ast_idx_range_in_tokenlist has params reversed
local fpos, lpos = M.ast_pos_range(ast, tokenlist)
if fpos then
return src:sub(fpos, lpos)
else
return nil
end
end
-- Gets smallest AST node in top_ast/tokenlist/src
-- completely containing position range [pos1, pos2].
-- careful: "function" is not part of the `Function node.
-- If range is inside comment, returns comment also.
-- If range is inside whitespace, then returns true in third return value.
-- CATEGORY: AST/tokenlist query
function M.smallest_ast_containing_range(top_ast, tokenlist, pos1, pos2)
local f0idx, l0idx = M.tokenlist_idx_range_over_pos_range(tokenlist, pos1, pos2)
-- Find enclosing AST.
M.ensure_parents_marked(top_ast)
local fidx, lidx = f0idx, l0idx
while tokenlist[fidx] and not tokenlist[fidx].ast.parent do fidx = fidx - 1 end
while tokenlist[lidx] and not tokenlist[lidx].ast.parent do lidx = lidx + 1 end
-- DEBUG(fidx, lidx, f0idx, l0idx, #tokenlist, pos1, pos2, tokenlist[fidx], tokenlist[lidx])
local ast = not (tokenlist[fidx] and tokenlist[lidx]) and top_ast or
M.common_ast_parent(tokenlist[fidx].ast, tokenlist[lidx].ast, top_ast)
-- DEBUG('m2', tokenlist[fidx], tokenlist[lidx], top_ast, ast, ast and ast.tag)
if l0idx == f0idx - 1 then -- whitespace
return ast, nil, true
elseif l0idx == f0idx and tokenlist[l0idx].tag == 'Comment' then
return ast, tokenlist[l0idx], nil
else
return ast, nil, nil
end
end
--IMPROVE: handle string edits and maybe others
-- Gets smallest statement block containing position pos or
-- nearest statement block before pos, whichever is smaller, given ast/tokenlist.
function M.current_statementblock(ast, tokenlist, pos)
local fidx,lidx = M.tokenlist_idx_range_over_pos_range(tokenlist, pos, pos)
if fidx > lidx then fidx = lidx end -- use nearest backward
-- Find closest AST node backward
while fidx >= 1 and tokenlist[fidx].tag == 'Comment' do fidx=fidx-1 end
if fidx < 1 then return ast, false end
local mast = tokenlist[fidx].ast
if not mast then return ast, false end
mast = M.get_containing_statementblock(mast, ast)
local isafter = false
if mast.tag2 ~= 'Block' then
local mfidx,mlidx = M.ast_idx_range_in_tokenlist(tokenlist, mast)
if pos > mlidx then
isafter = true
end
end
return mast, isafter
end
-- Gets index of bast in ast (nil if not found).
-- CATEGORY: AST query
function M.ast_idx(ast, bast)
for idx=1,#ast do
if ast[idx] == bast then return idx end
end
return nil
end
-- Gets parent of ast and index of ast in parent.
-- Root node top_ast must also be provided. Returns nil, nil if ast is root.
-- Note: may call mark_parents.
-- CATEGORY: AST query
function M.ast_parent_idx(top_ast, ast)
if ast == top_ast then return nil, nil end
M.ensure_parents_marked(top_ast); assert(ast.parent)
local idx = M.ast_idx(ast.parent, ast)
return ast.parent, idx
end
-- Gets common parent of aast and bast. Always returns value.
-- Must provide root top_ast too.
-- CATEGORY: AST query
function M.common_ast_parent(aast, bast, top_ast)
M.ensure_parents_marked(top_ast)
local isparent = {}
local tast = bast; repeat isparent[tast] = true; tast = tast.parent until not tast
local uast = aast; repeat if isparent[uast] then return uast end; uast = uast.parent until not uast
assert(false)
end
-- Replaces old_ast with new_ast/new_tokenlist in top_ast/tokenlist.
-- Note: assumes new_ast is a block. assumes old_ast is a statement or block.
-- CATEGORY: AST/tokenlist
function M.replace_statements(top_ast, tokenlist, old_ast, new_ast, new_tokenlist)
remove_ast_in_tokenlist(tokenlist, old_ast)
insert_tokenlist(tokenlist, new_tokenlist)
if old_ast == top_ast then -- special case: no parent
M.switchtable(old_ast, new_ast) -- note: safe since block is not in tokenlist.
else
local parent_ast, idx = M.ast_parent_idx(top_ast, old_ast)
table.remove(parent_ast, idx)
tinsertlist(parent_ast, idx, new_ast)
end
-- fixup annotations
for _,bast in ipairs(new_ast) do
if top_ast.tag2 then M.mark_tag2(bast, bast.tag == 'Do' and 'StatBlock' or 'Block') end
if old_ast.parent then M.mark_parents(bast, old_ast.parent) end
end
end
-- Adjusts lineinfo in tokenlist.
-- All char positions starting at pos1 are shifted by delta number of chars.
-- CATEGORY: tokenlist
function M.adjust_lineinfo(tokenlist, pos1, delta)
for _,token in ipairs(tokenlist) do
if token.fpos >= pos1 then
token.fpos = token.fpos + delta
end
if token.lpos >= pos1 then
token.lpos = token.lpos + delta
end
end
--tokenlist.nbytes = tokenlist.nbytes + delta
end
-- For each node n in ast, sets n.parent to parent node of n.
-- Assumes ast.parent will be parent_ast (may be nil)
-- CATEGORY: AST query
function M.mark_parents(ast, parent_ast)
ast.parent = parent_ast
for _,ast2 in ipairs(ast) do
if type(ast2) == 'table' then
M.mark_parents(ast2, ast)
end
end
end
-- Calls mark_parents(ast) if ast not marked.
-- CATEGORY: AST query
function M.ensure_parents_marked(ast)
if ast[1] and not ast[1].parent then M.mark_parents(ast) end
end
-- For each node n in ast, sets n.tag2 to context string:
-- 'Block' - node is block
-- 'Stat' - node is statement
-- 'StatBlock' - node is statement and block (i.e. `Do)
-- 'Exp' - node is expression
-- 'Explist' - node is expression list (or identifier list)
-- 'Pair' - node is key-value pair in table constructor
-- note: ast.tag2 will be set to context.
-- CATEGORY: AST query
local iscertainstat = {Do=true, Set=true, While=true, Repeat=true, If=true,
Fornum=true, Forin=true, Local=true, Localrec=true, Return=true, Break=true}
function M.mark_tag2(ast, context)
context = context or 'Block'
ast.tag2 = context
for i,bast in ipairs(ast) do
if type(bast) == 'table' then
local nextcontext
if bast.tag == 'Do' then
nextcontext = 'StatBlock'
elseif iscertainstat[bast.tag] then
nextcontext = 'Stat'
elseif bast.tag == 'Call' or bast.tag == 'Invoke' then
nextcontext = context == 'Block' and 'Stat' or 'Exp'
--DESIGN:Metalua: these calls actually contain expression lists,
-- but the expression list is not represented as a complete node
-- by Metalua (as blocks are in `Do statements)
elseif bast.tag == 'Pair' then
nextcontext = 'Pair'
elseif not bast.tag then
if ast.tag == 'Set' or ast.tag == 'Local' or ast.tag == 'Localrec'
or ast.tag == 'Forin' and i <= 2
or ast.tag == 'Function' and i == 1
then
nextcontext = 'Explist'
else
nextcontext = 'Block'
end
else
nextcontext = 'Exp'
end
M.mark_tag2(bast, nextcontext)
end
end
end
-- Gets smallest statement or block containing or being `ast`.
-- The AST root node `top_ast` must also be provided.
-- Note: may decorate AST as side-effect (mark_tag2/mark_parents).
-- top_ast is assumed a block, so this is always successful.
-- CATEGORY: AST query
function M.get_containing_statementblock(ast, top_ast)
if not top_ast.tag2 then M.mark_tag2(top_ast) end
if ast.tag2 == 'Stat' or ast.tag2 == 'StatBlock' or ast.tag2 == 'Block' then
return ast
else
M.ensure_parents_marked(top_ast)
return M.get_containing_statementblock(ast.parent, top_ast)
end
end
-- Finds smallest statement, block, or comment AST in ast/tokenlist containing position
-- range [fpos, lpos]. If allowexpand is true (default nil) and located AST
-- coincides with position range, then next containing statement is used
-- instead (this allows multiple calls to further expand the statement selection).
-- CATEGORY: AST query
function M.select_statementblockcomment(ast, tokenlist, fpos, lpos, allowexpand)
--IMPROVE: rename ast to top_ast
local match_ast, comment_ast = M.smallest_ast_containing_range(ast, tokenlist, fpos, lpos)
local select_ast = comment_ast or M.get_containing_statementblock(match_ast, ast)
local nfpos, nlpos = M.ast_pos_range(select_ast, tokenlist)
--DEBUG('s', nfpos, nlpos, fpos, lpos, match_ast.tag, select_ast.tag)
if allowexpand and fpos == nfpos and lpos == nlpos then
if comment_ast then
-- Select enclosing statement.
select_ast = match_ast
nfpos, nlpos = M.ast_pos_range(select_ast, tokenlist)
else
-- note: multiple times may be needed to expand selection. For example, in
-- `for x=1,2 do f() end` both the statement `f()` and block `f()` have
-- the same position range.
M.ensure_parents_marked(ast)
while select_ast.parent and fpos == nfpos and lpos == nlpos do
select_ast = M.get_containing_statementblock(select_ast.parent, ast)
nfpos, nlpos = M.ast_pos_range(select_ast, tokenlist)
end
end
end
return nfpos, nlpos
end
-- Converts tokenlist to string representation for debugging.
-- CATEGORY: tokenlist debug
function M.dump_tokenlist(tokenlist)
local ts = {}
for i,token in ipairs(tokenlist) do
ts[#ts+1] = 'tok.' .. i .. ': [' .. token.fpos .. ',' .. token.lpos .. '] '
.. tostring(token[1]) .. ' ' .. tostring(token.ast.tag)
end
return table.concat(ts, '\n') -- .. 'nbytes=' .. tokenlist.nbytes .. '\n'
end
--FIX:Q: does this handle Unicode ok?
--FIX?:Metalua: fails on string with escape sequence '\/'. The Reference Manual
-- doesn't say this sequence is valid though.
--FIX:Metalua: In `local --[[x]] function --[[y]] f() end`,
-- 'x' comment omitted from AST.
--FIX:Metalua: `do --[[x]] end` doesn't generate comments in AST.
-- `if x then --[[x]] end` and `while 1 do --[[x]] end` generates
-- comments in first/last of block
--FIX:Metalua: `--[[x]] f() --[[y]]` returns lineinfo around `f()`.
-- `--[[x]] --[[y]]` returns lineinfo around everything.
--FIX:Metalua: `while 1 do --[[x]] --[[y]] end` returns first > last
-- lineinfo for contained block
--FIX:Metalua: search for "PATCHED:LuaInspect" in the metalualib folder.
--FIX?:Metalua: loadstring parses "--x" but metalua omits the comment in the AST
--FIX?:Metalua: `local x` is generating `Local{{`Id{x}}, {}}`, which
-- has no lineinfo on {}. This is contrary to the Metalua
-- spec: `Local{ {ident+} {expr+}? }.
-- Other things like `self` also generate no lineinfo.
-- The ast2.lineinfo above avoids this.
--FIX:Metalua: Metalua shouldn't overwrite ipairs/pairs. Note: Metalua version
-- doesn't set errorlevel correctly.
--Q:Metalua: Why does `return --[[y]] z --[[x]]` have
-- lineinfo.first.comments, lineinfo.last.comments,
-- plus lineinfo.comments (which is the same as lineinfo.first.comments) ?
--CAUTION:Metalua: `do f() end` returns lineinfo around `do f() end`, while
-- `while 1 do f() end` returns lineinfo around `f()` for inner block.
--CAUTION:Metalua: The lineinfo on Metalua comments is inconsistent with other
-- nodes
--CAUTION:Metalua: lineinfo of table in `f{}` is [3,2], of `f{ x,y }` it's [4,6].
-- This is inconsistent with `x={}` which is [3,4] and `f""` which is [1,2]
-- for the string.
--CAUTION:Metalua: only the `function()` form of `Function includes `function`
-- in lineinfo. 'function' is part of `Localrec and `Set in syntactic sugar form.
--[=[TESTSUITE
-- test longest_prefix/longest_postfix
local function pr(text1, text2)
local lastv
local function same(v)
assert(not lastv or v == lastv); lastv = v; return v
end
local function test1(text1, text2) -- test prefix/postfix
same(longest_prefix(text1, text2))
same(longest_postfix(text1:reverse(), text2:reverse()))
end
local function test2(text1, text2) -- test swap
test1(text1, text2)
test1(text2, text1)
end
for _,extra in ipairs{"", "x", "xy", "xyz"} do -- test extra chars
test2(text1, text2..extra)
test2(text2, text1..extra)
end
return lastv
end
check('==', pr("",""), 0)
check('==', pr("a",""), 0)
check('==', pr("a","a"), 1)
check('==', pr("ab",""), 0)
check('==', pr("ab","a"), 1)
check('==', pr("ab","ab"), 2)
check('==', pr("abcdefg","abcdefgh"), 7)
--]=]
--[=[TESTSUITE
print 'DONE'
--]=]
return M

View File

@ -1,390 +0,0 @@
--[[
compat_env v$(_VERSION) - Lua 5.1/5.2 environment compatibility functions
SYNOPSIS
-- Get load/loadfile compatibility functions only if using 5.1.
local CL = pcall(load, '') and _G or require 'compat_env'
local load = CL.load
local loadfile = CL.loadfile
-- The following now works in both Lua 5.1 and 5.2:
assert(load('return 2*pi', nil, 't', {pi=math.pi}))()
assert(loadfile('ex.lua', 't', {print=print}))()
-- Get getfenv/setfenv compatibility functions only if using 5.2.
local getfenv = _G.getfenv or require 'compat_env'.getfenv
local setfenv = _G.setfenv or require 'compat_env'.setfenv
local function f() return x end
setfenv(f, {x=2})
print(x, getfenv(f).x) --> 2, 2
DESCRIPTION
This module provides Lua 5.1/5.2 environment related compatibility functions.
This includes implementations of Lua 5.2 style `load` and `loadfile`
for use in Lua 5.1. It also includes Lua 5.1 style `getfenv` and `setfenv`
for use in Lua 5.2.
API
local CL = require 'compat_env'
CL.load (ld [, source [, mode [, env] ] ]) --> f [, err]
This behaves the same as the Lua 5.2 `load` in both
Lua 5.1 and 5.2.
http://www.lua.org/manual/5.2/manual.html#pdf-load
CL.loadfile ([filename [, mode [, env] ] ]) --> f [, err]
This behaves the same as the Lua 5.2 `loadfile` in both
Lua 5.1 and 5.2.
http://www.lua.org/manual/5.2/manual.html#pdf-loadfile
CL.getfenv ([f]) --> t
This is identical to the Lua 5.1 `getfenv` in Lua 5.1.
This behaves similar to the Lua 5.1 `getfenv` in Lua 5.2.
When a global environment is to be returned, or when `f` is a
C function, this returns `_G` since Lua 5.2 doesn't have
(thread) global and C function environments. This will also
return `_G` if the Lua function `f` lacks an `_ENV`
upvalue, but it will raise an error if uncertain due to lack of
debug info. It is not normally considered good design to use
this function; when possible, use `load` or `loadfile` instead.
http://www.lua.org/manual/5.1/manual.html#pdf-getfenv
CL.setfenv (f, t)
This is identical to the Lua 5.1 `setfenv` in Lua 5.1.
This behaves similar to the Lua 5.1 `setfenv` in Lua 5.2.
This will do nothing if `f` is a Lua function that
lacks an `_ENV` upvalue, but it will raise an error if uncertain
due to lack of debug info. See also Design Notes below.
It is not normally considered good design to use
this function; when possible, use `load` or `loadfile` instead.
http://www.lua.org/manual/5.1/manual.html#pdf-setfenv
DESIGN NOTES
This module intends to provide robust and fairly complete reimplementations
of the environment related Lua 5.1 and Lua 5.2 functions.
No effort is made, however, to simulate rare or difficult to simulate features,
such as thread environments, although this is liable to change in the future.
Such 5.1 capabilities are discouraged and ideally
removed from 5.1 code, thereby allowing your code to work in both 5.1 and 5.2.
In Lua 5.2, a `setfenv(f, {})`, where `f` lacks any upvalues, will be silently
ignored since there is no `_ENV` in this function to write to, and the
environment will have no effect inside the function anyway. However,
this does mean that `getfenv(setfenv(f, t))` does not necessarily equal `t`,
which is incompatible with 5.1 code (a possible workaround would be [1]).
If `setfenv(f, {})` has an upvalue but no debug info, then this will raise
an error to prevent inadvertently executing potentially untrusted code in the
global environment.
It is not normally considered good design to use `setfenv` and `getfenv`
(one reason they were removed in 5.2). When possible, consider replacing
these with `load` or `loadfile`, which are more restrictive and have native
implementations in 5.2.
This module might be merged into a more general Lua 5.1/5.2 compatibility
library (e.g. a full reimplementation of Lua 5.2 `_G`). However,
`load/loadfile/getfenv/setfenv` perhaps are among the more cumbersome
functions not to have.
INSTALLATION
Download compat_env.lua:
wget https://raw.github.com/gist/1654007/compat_env.lua
Copy compat_env.lua into your LUA_PATH.
Alternately, unpack, test, and install into LuaRocks:
wget https://raw.github.com/gist/1422205/sourceunpack.lua
lua sourceunpack.lua compat_env.lua
(cd out && luarocks make)
Related work
http://lua-users.org/wiki/LuaVersionCompatibility
https://github.com/stevedonovan/Penlight/blob/master/lua/pl/utils.lua
- penlight implementations of getfenv/setfenv
http://lua-users.org/lists/lua-l/2010-06/msg00313.html
- initial getfenv/setfenv implementation
References
[1] http://lua-users.org/lists/lua-l/2010-06/msg00315.html
Copyright
(c) 2012 David Manura. Licensed under the same terms as Lua 5.1/5.2 (MIT license).
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
--]]---------------------------------------------------------------------
local M = {_TYPE='module', _NAME='compat_env', _VERSION='0.2.20120124'}
local function check_chunk_type(s, mode)
local nmode = mode or 'bt'
local is_binary = s and #s > 0 and s:byte(1) == 27
if is_binary and not nmode:match'b' then
return nil, ("attempt to load a binary chunk (mode is '%s')"):format(mode)
elseif not is_binary and not nmode:match't' then
return nil, ("attempt to load a text chunk (mode is '%s')"):format(mode)
end
return true
end
local IS_52_LOAD = pcall(load, '')
if IS_52_LOAD then
M.load = _G.load
M.loadfile = _G.loadfile
else
-- 5.2 style `load` implemented in 5.1
function M.load(ld, source, mode, env)
local f
if type(ld) == 'string' then
local s = ld
local ok, err = check_chunk_type(s, mode); if not ok then return ok, err end
local err; f, err = loadstring(s, source); if not f then return f, err end
elseif type(ld) == 'function' then
local ld2 = ld
if (mode or 'bt') ~= 'bt' then
local first = ld()
local ok, err = check_chunk_type(first, mode); if not ok then return ok, err end
ld2 = function()
if first then
local chunk=first; first=nil; return chunk
else return ld() end
end
end
local err; f, err = load(ld2, source); if not f then return f, err end
else
error(("bad argument #1 to 'load' (function expected, got %s)"):format(type(ld)), 2)
end
if env then setfenv(f, env) end
return f
end
-- 5.2 style `loadfile` implemented in 5.1
function M.loadfile(filename, mode, env)
if (mode or 'bt') ~= 'bt' then
local ioerr
local fh, err = io.open(filename, 'rb'); if not fh then return fh, err end
local function ld() local chunk; chunk,ioerr = fh:read(4096); return chunk end
local f, err = M.load(ld, filename and '@'..filename, mode, env)
fh:close()
if not f then return f, err end
if ioerr then return nil, ioerr end
return f
else
local f, err = loadfile(filename); if not f then return f, err end
if env then setfenv(f, env) end
return f
end
end
end
if _G.setfenv then -- Lua 5.1
M.setfenv = _G.setfenv
M.getfenv = _G.getfenv
else -- >= Lua 5.2
-- helper function for `getfenv`/`setfenv`
local function envlookup(f)
local name, val
local up = 0
local unknown
repeat
up=up+1; name, val = debug.getupvalue(f, up)
if name == '' then unknown = true end
until name == '_ENV' or name == nil
if name ~= '_ENV' then
up = nil
if unknown then error("upvalues not readable in Lua 5.2 when debug info missing", 3) end
end
return (name == '_ENV') and up, val, unknown
end
-- helper function for `getfenv`/`setfenv`
local function envhelper(f, name)
if type(f) == 'number' then
if f < 0 then
error(("bad argument #1 to '%s' (level must be non-negative)"):format(name), 3)
elseif f < 1 then
error("thread environments unsupported in Lua 5.2", 3) --[*]
end
f = debug.getinfo(f+2, 'f').func
elseif type(f) ~= 'function' then
error(("bad argument #1 to '%s' (number expected, got %s)"):format(type(name, f)), 2)
end
return f
end
-- [*] might simulate with table keyed by coroutine.running()
-- 5.1 style `setfenv` implemented in 5.2
function M.setfenv(f, t)
local f = envhelper(f, 'setfenv')
local up, val, unknown = envlookup(f)
if up then
debug.upvaluejoin(f, up, function() return up end, 1) -- unique upvalue [*]
debug.setupvalue(f, up, t)
else
local what = debug.getinfo(f, 'S').what
if what ~= 'Lua' and what ~= 'main' then -- not Lua func
error("'setfenv' cannot change environment of given object", 2)
end -- else ignore no _ENV upvalue (warning: incompatible with 5.1)
end
end
-- [*] http://lua-users.org/lists/lua-l/2010-06/msg00313.html
-- 5.1 style `getfenv` implemented in 5.2
function M.getfenv(f)
if f == 0 or f == nil then return _G end -- simulated behavior
local f = envhelper(f, 'setfenv')
local up, val = envlookup(f)
if not up then return _G end -- simulated behavior [**]
return val
end
-- [**] possible reasons: no _ENV upvalue, C function
end
return M
--[[ FILE rockspec.in
package = 'compat_env'
version = '$(_VERSION)-1'
source = {
url = 'https://raw.github.com/gist/1654007/$(GITID)/compat_env.lua',
--url = 'https://raw.github.com/gist/1654007/compat_env.lua', -- latest raw
--url = 'https://gist.github.com/gists/1654007/download',
md5 = '$(MD5)'
}
description = {
summary = 'Lua 5.1/5.2 environment compatibility functions',
detailed = [=[
Provides Lua 5.1/5.2 environment related compatibility functions.
This includes implementations of Lua 5.2 style `load` and `loadfile`
for use in Lua 5.1. It also includes Lua 5.1 style `getfenv` and `setfenv`
for use in Lua 5.2.
]=],
license = 'MIT/X11',
homepage = 'https://gist.github.com/1654007',
maintainer = 'David Manura'
}
dependencies = {} -- Lua 5.1 or 5.2
build = {
type = 'builtin',
modules = {
['compat_env'] = 'compat_env.lua'
}
}
--]]---------------------------------------------------------------------
--[[ FILE test.lua
-- test.lua - test suite for compat_env module.
local CL = require 'compat_env'
local load = CL.load
local loadfile = CL.loadfile
local setfenv = CL.setfenv
local getfenv = CL.getfenv
local function checkeq(a, b, e)
if a ~= b then error(
'not equal ['..tostring(a)..'] ['..tostring(b)..'] ['..tostring(e)..']')
end
end
local function checkerr(pat, ok, err)
assert(not ok, 'checkerr')
assert(type(err) == 'string' and err:match(pat), err)
end
-- test `load`
checkeq(load('return 2')(), 2)
checkerr('expected near', load'return 2 2')
checkerr('text chunk', load('return 2', nil, 'b'))
checkerr('text chunk', load('', nil, 'b'))
checkerr('binary chunk', load('\027', nil, 't'))
checkeq(load('return 2*x',nil,'bt',{x=5})(), 10)
checkeq(debug.getinfo(load('')).source, '')
checkeq(debug.getinfo(load('', 'foo')).source, 'foo')
-- test `loadfile`
local fh = assert(io.open('tmp.lua', 'wb'))
fh:write('return (...) or x')
fh:close()
checkeq(loadfile('tmp.lua')(2), 2)
checkeq(loadfile('tmp.lua', 't')(2), 2)
checkerr('text chunk', loadfile('tmp.lua', 'b'))
checkeq(loadfile('tmp.lua', nil, {x=3})(), 3)
checkeq(debug.getinfo(loadfile('tmp.lua')).source, '@tmp.lua')
checkeq(debug.getinfo(loadfile('tmp.lua', 't', {})).source, '@tmp.lua')
os.remove'tmp.lua'
-- test `setfenv`/`getfenv`
x = 5
local a,b=true; local function f(c) if a then return x,b,c end end
setfenv(f, {x=3})
checkeq(f(), 3)
checkeq(getfenv(f).x, 3)
checkerr('cannot change', pcall(setfenv, string.len, {})) -- C function
checkeq(getfenv(string.len), _G) -- C function
local function g()
setfenv(1, {x=4})
checkeq(getfenv(1).x, 4)
return x
end
checkeq(g(), 4) -- numeric level
if _G._VERSION ~= 'Lua 5.1' then
checkerr('unsupported', pcall(setfenv, 0, {}))
end
checkeq(getfenv(0), _G)
checkeq(getfenv(), _G) -- no arg
checkeq(x, 5) -- main unaltered
setfenv(function()end, {}) -- no upvalues, ignore
checkeq(getfenv(function()end), _G) -- no upvaluse
if _G._VERSION ~= 'Lua 5.1' then
checkeq(getfenv(setfenv(function()end, {})), _G) -- warning: incompatible with 5.1
end
x = nil
print 'OK'
--]]---------------------------------------------------------------------
--[[ FILE CHANGES.txt
0.2.20120124
Renamed module to compat_env (from compat_load)
Add getfenv/setfenv functions
0.1.20120121
Initial public release
--]]

View File

@ -1,90 +0,0 @@
-- Recursive object dumper, for debugging.
-- (c) 2010 David Manura, MIT License.
local M = {}
-- My own object dumper.
-- Intended for debugging, not serialization, with compact formatting.
-- Robust against recursion.
-- Renders Metalua table tag fields specially {tag=X, ...} --> "`X{...}".
-- On first call, only pass parameter o.
-- CATEGORY: AST debug
local ignore_keys_ = {lineinfo=true}
local norecurse_keys_ = {parent=true, ast=true}
local function dumpstring_key_(k, isseen, newindent)
local ks = type(k) == 'string' and k:match'^[%a_][%w_]*$' and k or
'[' .. M.dumpstring(k, isseen, newindent) .. ']'
return ks
end
local function sort_keys_(a, b)
if type(a) == 'number' and type(b) == 'number' then
return a < b
elseif type(a) == 'number' then
return false
elseif type(b) == 'number' then
return true
elseif type(a) == 'string' and type(b) == 'string' then
return a < b
else
return tostring(a) < tostring(b) -- arbitrary
end
end
function M.dumpstring(o, isseen, indent, key)
isseen = isseen or {}
indent = indent or ''
if type(o) == 'table' then
if isseen[o] or norecurse_keys_[key] then
return (type(o.tag) == 'string' and '`' .. o.tag .. ':' or '') .. tostring(o)
else isseen[o] = true end -- avoid recursion
local used = {}
local tag = o.tag
local s = '{'
if type(o.tag) == 'string' then
s = '`' .. tag .. s; used['tag'] = true
end
local newindent = indent .. ' '
local ks = {}; for k in pairs(o) do ks[#ks+1] = k end
table.sort(ks, sort_keys_)
--for i,k in ipairs(ks) do print ('keys', k) end
local forcenummultiline
for k in pairs(o) do
if type(k) == 'number' and type(o[k]) == 'table' then forcenummultiline = true end
end
-- inline elements
for _,k in ipairs(ks) do
if used[k] then -- skip
elseif ignore_keys_[k] then used[k] = true
elseif (type(k) ~= 'number' or not forcenummultiline) and
type(k) ~= 'table' and (type(o[k]) ~= 'table' or norecurse_keys_[k])
then
s = s .. dumpstring_key_(k, isseen, newindent) .. '=' .. M.dumpstring(o[k], isseen, newindent, k) .. ', '
used[k] = true
end
end
-- elements on separate lines
local done
for _,k in ipairs(ks) do
if not used[k] then
if not done then s = s .. '\n'; done = true end
s = s .. newindent .. dumpstring_key_(k, isseen) .. '=' .. M.dumpstring(o[k], isseen, newindent, k) .. ',\n'
end
end
s = s:gsub(',(%s*)$', '%1')
s = s .. (done and indent or '') .. '}'
return s
elseif type(o) == 'string' then
return string.format('%q', o)
else
return tostring(o)
end
end
return M

View File

@ -1,222 +0,0 @@
-- LuaInspect.globals - identifier scope analysis
-- Locates locals, globals, and their definitions.
--
-- (c) D.Manura, 2008-2010, MIT license.
-- based on http://lua-users.org/wiki/DetectingUndefinedVariables
local M = {}
--! require 'luainspect.typecheck' (context)
local LA = require "luainspect.ast"
local function definelocal(scope, name, ast)
if scope[name] then
scope[name].localmasked = true
ast.localmasking = scope[name]
end
scope[name] = ast
if name == '_' then ast.isignore = true end
end
-- Resolves scoping and usages of variable in AST.
-- Data Notes:
-- ast.localdefinition refers to lexically scoped definition of `Id node `ast`.
-- If ast.localdefinition == ast then ast is a "lexical definition".
-- If ast.localdefinition == nil, then variable is global.
-- ast.functionlevel is the number of functions the AST is contained in.
-- ast.functionlevel is defined iff ast is a lexical definition.
-- ast.isparam is true iff ast is a lexical definition and a function parameter.
-- ast.isset is true iff ast is a lexical definition and exists an assignment on it.
-- ast.isused is true iff ast is a lexical definition and has been referred to.
-- ast.isignore is true if local variable should be ignored (e.g. typically "_")
-- ast.localmasking - for a lexical definition, this is set to the lexical definition
-- this is masking (i.e. same name). nil if not masking.
-- ast.localmasked - true iff lexical definition masked by another lexical definition.
-- ast.isfield is true iff `String node ast is used for field access on object,
-- e.g. x.y or x['y'].z
-- ast.previous - For `Index{o,s} or `Invoke{o,s,...}, s.previous == o
local function traverse(ast, scope, globals, level, functionlevel)
scope = scope or {}
local blockrecurse
ast.level = level
-- operations on walking down the AST
if ast.tag == 'Local' then
blockrecurse = 1
-- note: apply new scope after processing values
elseif ast.tag == 'Localrec' then
local namelist_ast, valuelist_ast = ast[1], ast[2]
for _,value_ast in ipairs(namelist_ast) do
assert(value_ast.tag == 'Id')
local name = value_ast[1]
local parentscope = getmetatable(scope).__index
definelocal(parentscope, name, value_ast)
value_ast.localdefinition = value_ast
value_ast.functionlevel = functionlevel
value_ast.level = level+1
end
blockrecurse = 1
elseif ast.tag == 'Id' then
local name = ast[1]
if scope[name] then
ast.localdefinition = scope[name]
ast.functionlevel = functionlevel
scope[name].isused = true
else -- global, do nothing
end
elseif ast.tag == 'Function' then
local paramlist_ast, body_ast = ast[1], ast[2]
functionlevel = functionlevel + 1
for _,param_ast in ipairs(paramlist_ast) do
local name = param_ast[1]
assert(param_ast.tag == 'Id' or param_ast.tag == 'Dots')
if param_ast.tag == 'Id' then
definelocal(scope, name, param_ast)
param_ast.localdefinition = param_ast
param_ast.functionlevel = functionlevel
param_ast.isparam = true
end
param_ast.level = level+1
end
blockrecurse = 1
elseif ast.tag == 'Set' then
local reflist_ast, valuelist_ast = ast[1], ast[2]
for _,ref_ast in ipairs(reflist_ast) do
if ref_ast.tag == 'Id' then
local name = ref_ast[1]
if scope[name] then
scope[name].isset = true
else
if not globals[name] then
globals[name] = {set=ref_ast}
end
end
end
ref_ast.level = level+1
end
--ENHANCE? We could differentiate assignments to x (which indicates that
-- x is not const) and assignments to a member of x (which indicates that
-- x is not a pointer to const) and assignments to any nested member of x
-- (which indicates that x it not a transitive const).
elseif ast.tag == 'Fornum' then
blockrecurse = 1
elseif ast.tag == 'Forin' then
blockrecurse = 1
end
-- recurse (depth-first search down the AST)
if ast.tag == 'Repeat' then
local block_ast, cond_ast = ast[1], ast[2]
local scope = scope
for _,stat_ast in ipairs(block_ast) do
scope = setmetatable({}, {__index = scope})
traverse(stat_ast, scope, globals, level+1, functionlevel)
end
scope = setmetatable({}, {__index = scope})
traverse(cond_ast, scope, globals, level+1, functionlevel)
elseif ast.tag == 'Fornum' then
local name_ast, block_ast = ast[1], ast[#ast]
-- eval value list in current scope
for i=2, #ast-1 do traverse(ast[i], scope, globals, level+1, functionlevel) end
-- eval body in next scope
local name = name_ast[1]
definelocal(scope, name, name_ast)
name_ast.localdefinition = name_ast
name_ast.functionlevel = functionlevel
traverse(block_ast, scope, globals, level+1, functionlevel)
elseif ast.tag == 'Forin' then
local namelist_ast, vallist_ast, block_ast = ast[1], ast[2], ast[3]
-- eval value list in current scope
traverse(vallist_ast, scope, globals, level+1, functionlevel)
-- eval body in next scope
for _,name_ast in ipairs(namelist_ast) do
local name = name_ast[1]
definelocal(scope, name, name_ast)
name_ast.localdefinition = name_ast
name_ast.functionlevel = functionlevel
name_ast.level = level+1
end
traverse(block_ast, scope, globals, level+1, functionlevel)
else -- normal
for i,v in ipairs(ast) do
if i ~= blockrecurse and type(v) == 'table' then
local scope = setmetatable({}, {__index = scope})
traverse(v, scope, globals, level+1, functionlevel)
end
end
end
-- operations on walking up the AST
if ast.tag == 'Local' then
-- Unlike Localrec, variables come into scope after evaluating values.
local namelist_ast, valuelist_ast = ast[1], ast[2]
for _,name_ast in ipairs(namelist_ast) do
assert(name_ast.tag == 'Id')
local name = name_ast[1]
local parentscope = getmetatable(scope).__index
definelocal(parentscope, name, name_ast)
name_ast.localdefinition = name_ast
name_ast.functionlevel = functionlevel
name_ast.level = level+1
end
elseif ast.tag == 'Index' then
if ast[2].tag == 'String' then
ast[2].isfield = true
ast[2].previous = ast[1]
end
elseif ast.tag == 'Invoke' then
assert(ast[2].tag == 'String')
ast[2].isfield = true
ast[2].previous = ast[1]
end
end
function M.globals(ast)
-- Default list of defined variables.
local scope = setmetatable({}, {})
local globals = {}
traverse(ast, scope, globals, 1, 1) -- Start check.
return globals
end
-- Gets locals in scope of statement of block ast. If isafter is true and ast is statement,
-- uses scope just after statement ast.
-- Assumes 'parent' attributes on ast are marked.
-- Returns table mapping name -> AST local definition.
function M.variables_in_scope(ast, isafter)
local scope = {}
local cast = ast
while cast.parent do
local midx = LA.ast_idx(cast.parent, cast)
for idx=1,midx do
local bast = cast.parent[idx]
if bast.tag == 'Localrec' or bast.tag == 'Local' and (idx < midx or isafter) then
local names_ast = bast[1]
for bidx=1,#names_ast do
local name_ast = names_ast[bidx]
local name = name_ast[1]
scope[name] = name_ast
end
elseif cast ~= ast and (bast.tag == 'For' or bast.tag == 'Forin' or bast.tag == 'Function') then
local names_ast = bast[1]
for bidx=1,#names_ast do
local name_ast = names_ast[bidx]
if name_ast.tag == 'Id' then --Q: or maybe `Dots should be included
local name = name_ast[1]
scope[name] = name_ast
end
end
end
end
cast = cast.parent
end
return scope
end
return M

View File

@ -1,433 +0,0 @@
local M = {}
local T = require "luainspect.types"
-- signatures of known globals
M.global_signatures = {
assert = "assert (v [, message])",
collectgarbage = "collectgarbage (opt [, arg])",
dofile = "dofile (filename)",
error = "error (message [, level])",
_G = "(table)",
getfenv = "getfenv ([f])",
getmetatable = "getmetatable (object)",
ipairs = "ipairs (t)",
load = "load (func [, chunkname])",
loadfile = "loadfile ([filename])",
loadstring = "loadstring (string [, chunkname])",
next = "next (table [, index])",
pairs = "pairs (t)",
pcall = "pcall (f, arg1, ...)",
print = "print (...)",
rawequal = "rawequal (v1, v2)",
rawget = "rawget (table, index)",
rawset = "rawset (table, index, value)",
select = "select (index, ...)",
setfenv = "setfenv (f, table)",
setmetatable = "setmetatable (table, metatable)",
tonumber = "tonumber (e [, base])",
tostring = "tostring (e)",
type = "type (v)",
unpack = "unpack (list [, i [, j]])",
_VERSION = "(string)",
xpcall = "xpcall (f, err)",
module = "module (name [, ...])",
require = "require (modname)",
coroutine = "(table) coroutine manipulation library",
debug = "(table) debug facilities library",
io = "(table) I/O library",
math = "(table) math functions libary",
os = "(table) OS facilities library",
package = "(table) package library",
string = "(table) string manipulation library",
table = "(table) table manipulation library",
["coroutine.create"] = "coroutine.create (f)",
["coroutine.resume"] = "coroutine.resume (co [, val1, ...])",
["coroutine.running"] = "coroutine.running ()",
["coroutine.status"] = "coroutine.status (co)",
["coroutine.wrap"] = "coroutine.wrap (f)",
["coroutine.yield"] = "coroutine.yield (...)",
["debug.debug"] = "debug.debug ()",
["debug.getfenv"] = "debug.getfenv (o)",
["debug.gethook"] = "debug.gethook ([thread])",
["debug.getinfo"] = "debug.getinfo ([thread,] function [, what])",
["debug.getlocal"] = "debug.getlocal ([thread,] level, local)",
["debug.getmetatable"] = "debug.getmetatable (object)",
["debug.getregistry"] = "debug.getregistry ()",
["debug.getupvalue"] = "debug.getupvalue (func, up)",
["debug.setfenv"] = "debug.setfenv (object, table)",
["debug.sethook"] = "debug.sethook ([thread,] hook, mask [, count])",
["debug.setlocal"] = "debug.setlocal ([thread,] level, local, value)",
["debug.setmetatable"] = "debug.setmetatable (object, table)",
["debug.setupvalue"] = "debug.setupvalue (func, up, value)",
["debug.traceback"] = "debug.traceback ([thread,] [message] [, level])",
["io.close"] = "io.close ([file])",
["io.flush"] = "io.flush ()",
["io.input"] = "io.input ([file])",
["io.lines"] = "io.lines ([filename])",
["io.open"] = "io.open (filename [, mode])",
["io.output"] = "io.output ([file])",
["io.popen"] = "io.popen (prog [, mode])",
["io.read"] = "io.read (...)",
["io.tmpfile"] = "io.tmpfile ()",
["io.type"] = "io.type (obj)",
["io.write"] = "io.write (...)",
["math.abs"] = "math.abs (x)",
["math.acos"] = "math.acos (x)",
["math.asin"] = "math.asin (x)",
["math.atan"] = "math.atan (x)",
["math.atan2"] = "math.atan2 (y, x)",
["math.ceil"] = "math.ceil (x)",
["math.cos"] = "math.cos (x)",
["math.cosh"] = "math.cosh (x)",
["math.deg"] = "math.deg (x)",
["math.exp"] = "math.exp (x)",
["math.floor"] = "math.floor (x)",
["math.fmod"] = "math.fmod (x, y)",
["math.frexp"] = "math.frexp (x)",
["math.huge"] = "math.huge",
["math.ldexp"] = "math.ldexp (m, e)",
["math.log"] = "math.log (x)",
["math.log10"] = "math.log10 (x)",
["math.max"] = "math.max (x, ...)",
["math.min"] = "math.min (x, ...)",
["math.modf"] = "math.modf (x)",
["math.pi"] = "math.pi",
["math.pow"] = "math.pow (x, y)",
["math.rad"] = "math.rad (x)",
["math.random"] = "math.random ([m [, n]])",
["math.randomseed"] = "math.randomseed (x)",
["math.sin"] = "math.sin (x)",
["math.sinh"] = "math.sinh (x)",
["math.sqrt"] = "math.sqrt (x)",
["math.tan"] = "math.tan (x)",
["math.tanh"] = "math.tanh (x)",
["os.clock"] = "os.clock ()",
["os.date"] = "os.date ([format [, time]])",
["os.difftime"] = "os.difftime (t2, t1)",
["os.execute"] = "os.execute ([command])",
["os.exit"] = "os.exit ([code])",
["os.getenv"] = "os.getenv (varname)",
["os.remove"] = "os.remove (filename)",
["os.rename"] = "os.rename (oldname, newname)",
["os.setlocale"] = "os.setlocale (locale [, category])",
["os.time"] = "os.time ([table])",
["os.tmpname"] = "os.tmpname ()",
["package.cpath"] = "package.cpath",
["package.loaded"] = "package.loaded",
["package.loaders"] = "package.loaders",
["package.loadlib"] = "package.loadlib (libname, funcname)",
["package.path"] = "package.path",
["package.preload"] = "package.preload",
["package.seeall"] = "package.seeall (module)",
["string.byte"] = "string.byte (s [, i [, j]])",
["string.char"] = "string.char (...)",
["string.dump"] = "string.dump (function)",
["string.find"] = "string.find (s, pattern [, init [, plain]])",
["string.format"] = "string.format (formatstring, ...)",
["string.gmatch"] = "string.gmatch (s, pattern)",
["string.gsub"] = "string.gsub (s, pattern, repl [, n])",
["string.len"] = "string.len (s)",
["string.lower"] = "string.lower (s)",
["string.match"] = "string.match (s, pattern [, init])",
["string.rep"] = "string.rep (s, n)",
["string.reverse"] = "string.reverse (s)",
["string.sub"] = "string.sub (s, i [, j])",
["string.upper"] = "string.upper (s)",
["table.concat"] = "table.concat (table [, sep [, i [, j]]])",
["table.insert"] = "table.insert (table, [pos,] value)",
["table.maxn"] = "table.maxn (table)",
["table.remove"] = "table.remove (table [, pos])",
["table.sort"] = "table.sort (table [, comp])",
}
-- utility function. Converts e.g. name 'math.sqrt' to its value.
local function resolve_global_helper_(name)
local o = _G
for fieldname in name:gmatch'[^%.]+' do o = o[fieldname] end
return o
end
local function resolve_global(name)
local a, b = pcall(resolve_global_helper_, name)
if a then return b else return nil, b end
end
-- Same as global_signatures but maps value (not name) to signature.
M.value_signatures = {}
local isobject = {['function']=true, ['table']=true, ['userdata']=true, ['coroutine']=true}
for name,sig in pairs(M.global_signatures) do
local val, err = resolve_global(name)
if isobject[type(val)] then
M.value_signatures[val] = sig
end
end
-- min,max argument counts.
M.argument_counts = {
[assert] = {1,2},
[collectgarbage] = {1,2},
[dofile] = {1},
[error] = {1,2},
[getfenv or false] = {0,1},
[getmetatable] = {1,1},
[ipairs] = {1,1},
[load] = {1,2},
[loadfile] = {0,1},
[loadstring] = {1,2},
[next] = {1,2},
[pairs] = {1,1},
[pcall] = {1,math.huge},
[print] = {0,math.huge},
[rawequal] = {2,2},
[rawget] = {2,2},
[rawset] = {3,3},
[select] = {1, math.huge},
[setfenv or false] = {2,2},
[setmetatable] = {2,2},
[tonumber] = {1,2},
[tostring] = {1},
[type] = {1},
[unpack] = {1,3},
[xpcall] = {2,2},
[module] = {1,math.huge},
[require] = {1,1},
[coroutine.create] = {1,1},
[coroutine.resume] = {1, math.huge},
[coroutine.running] = {0,0},
[coroutine.status] = {1,1},
[coroutine.wrap] = {1,1},
[coroutine.yield] = {0,math.huge},
[debug.debug] = {0,0},
[debug.getfenv or false] = {1,1},
[debug.gethook] = {0,1},
[debug.getinfo] = {1,3},
[debug.getlocal] = {2,3},
[debug.getmetatable] = {1,1},
[debug.getregistry] = {0,0},
[debug.getupvalue] = {2,2},
[debug.setfenv or false] = {2,2},
[debug.sethook] = {2,4},
[debug.setlocal] = {3,4},
[debug.setmetatable] = {2,2},
[debug.setupvalue] = {3,3},
[debug.traceback] = {0,3},
[io.close] = {0,1},
[io.flush] = {0,0},
[io.input] = {0,1},
[io.lines] = {0,1},
[io.open] = {1,2},
[io.output] = {0,1},
[io.popen] = {1,2},
[io.read] = {0,math.huge},
[io.tmpfile] = {0},
[io.type] = {1},
[io.write] = {0,math.huge},
[math.abs] = {1},
[math.acos] = {1},
[math.asin] = {1},
[math.atan] = {1},
[math.atan2] = {2,2},
[math.ceil] = {1,1},
[math.cos] = {1,1},
[math.cosh] = {1,1},
[math.deg] = {1,1},
[math.exp] = {1,1},
[math.floor] = {1,1},
[math.fmod] = {2,2},
[math.frexp] = {1,1},
[math.ldexp] = {2,2},
[math.log] = {1,1},
[math.log10] = {1,1},
[math.max] = {1,math.huge},
[math.min] = {1,math.huge},
[math.modf] = {1,1},
[math.pow] = {2,2},
[math.rad] = {1,1},
[math.random] = {0,2},
[math.randomseed] = {1,1},
[math.sin] = {1,1},
[math.sinh] = {1,1},
[math.sqrt] = {1,1},
[math.tan] = {1,1},
[math.tanh] = {1,1},
[os.clock] = {0,0},
[os.date] = {0,2},
[os.difftime] = {2,2},
[os.execute] = {0,1},
[os.exit] = {0,1},
[os.getenv] = {1,1},
[os.remove] = {1,1},
[os.rename] = {2,2},
[os.setlocale] = {1,2},
[os.time] = {0,1},
[os.tmpname] = {0,0},
[package.loadlib] = {2,2},
[package.seeall] = {1,1},
[string.byte] = {1,3},
[string.char] = {0,math.huge},
[string.dump] = {1,1},
[string.find] = {2,4},
[string.format] = {1,math.huge},
[string.gmatch] = {2,2},
[string.gsub] = {3,4},
[string.len] = {1,1},
[string.lower] = {1,1},
[string.match] = {2,3},
[string.rep] = {2,2},
[string.reverse] = {1,1},
[string.sub] = {2,3},
[string.upper] = {1,1},
[table.concat] = {1,4},
[table.insert] = {2,3},
[table.maxn] = {1,1},
[table.remove] = {1,2},
[table.sort] = {1,2},
[false] = nil -- trick (relies on potentially undefined behavior)
}
-- functions with zero or nearly zero side-effects, and with deterministic results, that may be evaluated by the analyzer.
M.safe_function = {
[require] = true,
[rawequal] = true,
[rawget] = true,
[require] = true, -- sort of
[select] = true,
[tonumber] = true,
[tostring] = true,
[type] = true,
[unpack] = true,
[coroutine.create] = true,
-- [coroutine.resume]
[coroutine.running] = true,
[coroutine.status] = true,
[coroutine.wrap] = true,
--[coroutine.yield]
-- [debug.debug]
--[debug.getfenv] = true,
[debug.gethook] = true,
[debug.getinfo] = true,
[debug.getlocal] = true,
[debug.getmetatable] = true,
[debug.getregistry] = true,
[debug.getupvalue] = true,
-- [debug.setfenv]
-- [debug.sethook]
-- [debug.setlocal]
-- [debug.setmetatable]
-- [debug.setupvalue]
-- [debug.traceback] = true,
[io.type] = true,
-- skip all other io.*
[math.abs] = true,
[math.acos] = true,
[math.asin] = true,
[math.atan] = true,
[math.atan2] = true,
[math.ceil] = true,
[math.cos] = true,
[math.cosh] = true,
[math.deg] = true,
[math.exp] = true,
[math.floor] = true,
[math.fmod] = true,
[math.frexp] = true,
[math.ldexp] = true,
[math.log] = true,
[math.log10] = true,
[math.max] = true,
[math.min] = true,
[math.modf] = true,
[math.pow] = true,
[math.rad] = true,
--[math.random]
--[math.randomseed]
[math.sin] = true,
[math.sinh] = true,
[math.sqrt] = true,
[math.tan] = true,
[math.tanh] = true,
[os.clock] = true, -- safe but non-deterministic
[os.date] = true,-- safe but non-deterministic
[os.difftime] = true,
--[os.execute]
--[os.exit]
[os.getenv] = true, -- though depends on environment
--[os.remove]
--[os.rename]
--[os.setlocale]
[os.time] = true, -- safe but non-deterministic
--[os.tmpname]
[string.byte] = true,
[string.char] = true,
[string.dump] = true,
[string.find] = true,
[string.format] = true,
[string.gmatch] = true,
[string.gsub] = true,
[string.len] = true,
[string.lower] = true,
[string.match] = true,
[string.rep] = true,
[string.reverse] = true,
[string.sub] = true,
[string.upper] = true,
[table.maxn] = true,
}
M.mock_functions = {}
-- TODO:IMPROVE
local function mockfunction(func, ...)
local inputs = {n=0}
local outputs = {n=0}
local isoutputs
for i=1,select('#', ...) do
local v = select(i, ...)
if type(v) == 'table' then v = v[1] end
if v == 'N' or v == 'I' then v = T.number end
if v == '->' then
isoutputs = true
elseif isoutputs then
outputs[#outputs+1] = v; outputs.n = outputs.n + 1
else
inputs[#inputs+1] = v; inputs.n = inputs.n + 1
end
end
M.mock_functions[func] = {inputs=inputs, outputs=outputs}
end
mockfunction(math.abs, 'N', '->', {'N',0,math.huge})
mockfunction(math.acos, {'N',-1,1}, '->', {'N',0,math.pi/2})
mockfunction(math.asin, {'N',-1,1}, '->', {'N',-math.pi/2,math.pi/2})
mockfunction(math.atan, {'N',-math.huge,math.huge}, '->',
{'N',-math.pi/2,math.pi/2})
--FIX atan2
mockfunction(math.ceil, 'N','->','I')
mockfunction(math.cos, 'N','->',{'N',-1,1})
mockfunction(math.cosh, 'N','->',{'N',1,math.huge})
mockfunction(math.deg, 'N','->','N')
mockfunction(math.exp, 'N','->',{'N',0,math.huge})
mockfunction(math.floor, 'N','->','I')
mockfunction(math.fmod, 'N','N','->','N')
mockfunction(math.frexp, 'N','->',{'N',-1,1},'->','I')
mockfunction(math.ldexp, {'N','I'},'->','N')
mockfunction(math.log, {'N',0,math.huge},'->','N')
mockfunction(math.log10, {'N',0,math.huge},'->','N')
-- function max(...) print 'NOT IMPL'end
-- function min(...) print 'NOT IMPL'end
mockfunction(math.modf, 'N','->','I',{'N',-1,1})
mockfunction(math.pow, 'N','N','->','N') -- improve?
mockfunction(math.rad, 'N','->','N')
-- random = function() print 'NOT IMPL' end
mockfunction(math.randomseed, 'N')
mockfunction(math.sin, 'N','->',{'N',-1,1})
mockfunction(math.sinh, 'N','->','N')
mockfunction(math.sqrt, {'N',0,math.huge},'->',{'N',0,math.huge})
mockfunction(math.tan, 'N','->','N') -- improve?
mockfunction(math.tanh, 'N','->',{'N',-1,1})
return M

View File

@ -1,40 +0,0 @@
-- luainspect.typecheck - Type definitions used to check LuaInspect itself.
--
-- (c) 2010 David Manura, MIT License.
local T = require "luainspect.types"
local ast_mt = {__tostring = function(s) return 'AST' end}
return function(context)
-- AST type.
local ast = T.table {
tag = T.string,
lineinfo=T.table{first=T.table{comments=T.table{T.table{T.string,T.number,T.number}},T.number,T.number,T.number,T.string},
ast=T.table{comments=T.table{T.table{T.string,T.number,T.number}},T.number,T.number,T.number,T.string}},
isfield=T.boolean, tag2=T.string,
value=T.universal, valueself=T.number, valuelist=T.table{n=T.number, isvaluepegged=T.boolean},
resolvedname=T.string, definedglobal=T.boolean, id=T.number, isparam=T.boolean, isset=T.boolean, isused=T.boolean,
isignore=T.boolean,
functionlevel=T.number, localmasked=T.boolean, note=T.string, nocollect=T.table{}, isdead=T.boolean}
-- FIX: some of these are "boolean or nil" actually
ast.localdefinition=ast; ast.localmasking = ast
ast.previous = ast; ast.parent = ast
ast.seevalue = ast; ast.seenote=ast
setmetatable(ast, ast_mt)
ast[1] = ast; ast[2] = ast
context.apply_value('ast$', ast)
-- Token type.
context.apply_value('token$', T.table{
tag=T.string, fpos=T.number, lpos=T.number, keywordid=T.number, ast=ast, [1]=T.string
})
-- Lua source code string type.
context.apply_value('src$', '')
-- SciTE syler object type.
local nf = function()end
context.apply_value('^styler$', T.table{SetState=nf, More=nf, Current=nf, Forward=nf, StartStyling=nf, EndStyling=nf, language=T.string})
end

View File

@ -1,130 +0,0 @@
local T = {} -- types
-- istype[o] iff o represents a type (i.e. set of values)
T.istype = {}
-- iserror[o] iff o represents an error type (created via T.error).
T.iserror = {}
-- istabletype[o] iff o represents a table type (created by T.table).
T.istabletype = {}
-- Number type
T.number = {}
setmetatable(T.number, T.number)
function T.number.__tostring(self)
return 'number'
end
T.istype[T.number] = true
-- String type
T.string = {}
setmetatable(T.string, T.string)
function T.string.__tostring(self)
return 'string'
end
T.istype[T.string] = true
-- Boolean type
T.boolean = {}
setmetatable(T.boolean, T.boolean)
function T.boolean.__tostring(self)
return 'boolean'
end
T.istype[T.boolean] = true
-- Table type
function T.table(t)
T.istype[t] = true
T.istabletype[t] = true
return t
end
-- Universal type. This is a superset of all other types.
T.universal = {}
setmetatable(T.universal, T.universal)
function T.universal.__tostring(self)
return 'unknown'
end
T.istype[T.universal] = true
-- nil type. Represents `nil` but can be stored in tables.
T['nil'] = {}
setmetatable(T['nil'], T['nil'])
T['nil'].__tostring = function(self)
return 'nil'
end
T.istype[T['nil']] = true
-- None type. Represents a non-existent value, in a similar way
-- that `none` is used differently from `nil` in the Lua C API.
T.none = {}
setmetatable(T.none, T.none)
function T.none.__tostring(self)
return 'none'
end
T.istype[T.none] = true
-- Error type
local CError = {}; CError.__index = CError
function CError.__tostring(self) return "error:" .. tostring(self.value) end
function T.error(val)
local self = setmetatable({value=val}, CError)
T.istype[self] = true
T.iserror[self] = true
return self
end
-- Gets a type that is a superset of the two given types.
function T.superset_types(a, b)
if T.iserror[a] then return a end
if T.iserror[b] then return b end
if rawequal(a, b) then -- note: including nil == nil
return a
elseif type(a) == 'string' or a == T.string then
if type(b) == 'string' or b == T.string then
return T.string
else
return T.universal
end
elseif type(a) == 'number' or a == T.number then
if type(b) == 'number' or b == T.number then
return T.number
else
return T.universal
end
elseif type(a) == 'boolean' or a == T.boolean then
if type(b) == 'boolean' or b == T.boolean then
return T.boolean
else
return T.universal
end
else
return T.universal -- IMPROVE
end
end
--[[TESTS:
assert(T.superset_types(2, 2) == 2)
assert(T.superset_types(2, 3) == T.number)
assert(T.superset_types(2, T.number) == T.number)
assert(T.superset_types(T.number, T.string) == T.universal)
print 'DONE'
--]]
-- Determines whether type `o` certainly evaluates to true (true),
-- certainly evaluates to false (false) or could evaluate to either
-- true of false ('?').
function T.boolean_cast(o)
if T.iserror[o] then -- special case
return '?'
elseif o == nil or o == false or o == T['nil'] then -- all subsets of {nil, false}
return false
elseif o == T.universal or o == T.boolean then -- all supersets of boolean
return '?'
else -- all subsets of universal - {nil, false}
return true
end
end
return T

View File

@ -1,162 +0,0 @@
---------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Convert between various code representation formats. Atomic
-- converters are written in extenso, others are composed automatically
-- by chaining the atomic ones together in a closure.
--
-- Supported formats are:
--
-- * srcfile: the name of a file containing sources.
-- * src: these sources as a single string.
-- * lexstream: a stream of lexemes.
-- * ast: an abstract syntax tree.
-- * proto: a (Yueliang) struture containing a high level
-- representation of bytecode. Largely based on the
-- Proto structure in Lua's VM
-- * bytecode: a string dump of the function, as taken by
-- loadstring() and produced by string.dump().
-- * function: an executable lua function in RAM.
--
--------------------------------------------------------------------------------
require 'checks'
local M = { }
--------------------------------------------------------------------------------
-- Order of the transformations. if 'a' is on the left of 'b', then a 'a' can
-- be transformed into a 'b' (but not the other way around).
-- M.sequence goes for numbers to format names, M.order goes from format
-- names to numbers.
--------------------------------------------------------------------------------
M.sequence = {
'srcfile', 'src', 'lexstream', 'ast', 'proto', 'bytecode', 'function' }
local arg_types = {
srcfile = { 'string', '?string' },
src = { 'string', '?string' },
lexstream = { 'lexer.stream', '?string' },
ast = { 'table', '?string' },
proto = { 'table', '?string' },
bytecode = { 'string', '?string' },
}
M.order= { }; for a,b in pairs(M.sequence) do M.order[b]=a end
local CONV = { } -- conversion metatable __index
function CONV :srcfile_to_src(x, name)
checks('metalua.compiler', 'string', '?string')
name = name or '@'..x
local f, msg = io.open (x, 'rb')
if not f then error(msg) end
local r, msg = f :read '*a'
if not r then error("Cannot read file '"..x.."': "..msg) end
f :close()
return r, name
end
function CONV :src_to_lexstream(src, name)
checks('metalua.compiler', 'string', '?string')
local r = self.parser.lexer :newstream (src, name)
return r, name
end
function CONV :lexstream_to_ast(lx, name)
checks('metalua.compiler', 'lexer.stream', '?string')
local r = self.parser.chunk(lx)
r.source = name
return r, name
end
local bytecode_compiler = nil -- cache to avoid repeated `pcall(require(...))`
local function get_bytecode_compiler()
if bytecode_compiler then return bytecode_compiler else
local status, result = pcall(require, 'metalua.compiler.bytecode')
if status then
bytecode_compiler = result
return result
elseif string.match(result, "not found") then
error "Compilation only available with full Metalua"
else error (result) end
end
end
function CONV :ast_to_proto(ast, name)
checks('metalua.compiler', 'table', '?string')
return get_bytecode_compiler().ast_to_proto(ast, name), name
end
function CONV :proto_to_bytecode(proto, name)
return get_bytecode_compiler().proto_to_bytecode(proto), name
end
function CONV :bytecode_to_function(bc, name)
checks('metalua.compiler', 'string', '?string')
return loadstring(bc, name)
end
-- Create all sensible combinations
for i=1,#M.sequence do
local src = M.sequence[i]
for j=i+2, #M.sequence do
local dst = M.sequence[j]
local dst_name = src.."_to_"..dst
local my_arg_types = arg_types[src]
local functions = { }
for k=i, j-1 do
local name = M.sequence[k].."_to_"..M.sequence[k+1]
local f = assert(CONV[name], name)
table.insert (functions, f)
end
CONV[dst_name] = function(self, a, b)
checks('metalua.compiler', unpack(my_arg_types))
for _, f in ipairs(functions) do
a, b = f(self, a, b)
end
return a, b
end
--printf("Created M.%s out of %s", dst_name, table.concat(n, ', '))
end
end
--------------------------------------------------------------------------------
-- This one goes in the "wrong" direction, cannot be composed.
--------------------------------------------------------------------------------
function CONV :function_to_bytecode(...) return string.dump(...) end
function CONV :ast_to_src(...)
require 'metalua.loader' -- ast_to_string isn't written in plain lua
return require 'metalua.compiler.ast_to_src' (...)
end
local MT = { __index=CONV, __type='metalua.compiler' }
function M.new()
local parser = require 'metalua.compiler.parser' .new()
local self = { parser = parser }
setmetatable(self, MT)
return self
end
return M

View File

@ -1,42 +0,0 @@
--------------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Fabien Fleutot - API and implementation
--
--------------------------------------------------------------------------------
-- Export all public APIs from sub-modules, squashed into a flat spacename
local MT = { __type='metalua.compiler.parser' }
local MODULE_REL_NAMES = { "annot.grammar", "expr", "meta", "misc",
"stat", "table", "ext" }
local function new()
local M = {
lexer = require "metalua.compiler.parser.lexer" ();
extensions = { } }
for _, rel_name in ipairs(MODULE_REL_NAMES) do
local abs_name = "metalua.compiler.parser."..rel_name
local extender = require (abs_name)
if not M.extensions[abs_name] then
if type (extender) == 'function' then extender(M) end
M.extensions[abs_name] = extender
end
end
return setmetatable(M, MT)
end
return { new = new }

Some files were not shown because too many files have changed in this diff Show More