mirror of
https://github.com/rclone/rclone.git
synced 2024-11-22 05:28:09 +08:00
lib: add plugin support
This enables loading plugins from RCLONE_PLUGIN_PATH if set.
This commit is contained in:
parent
349112df6b
commit
44b603d2bd
|
@ -376,3 +376,37 @@ Add your fs to the docs - you'll need to pick an icon for it from [fontawesome](
|
||||||
* `docs/content/about.md` - front page of rclone.org
|
* `docs/content/about.md` - front page of rclone.org
|
||||||
* `docs/layouts/chrome/navbar.html` - add it to the website navigation
|
* `docs/layouts/chrome/navbar.html` - add it to the website navigation
|
||||||
* `bin/make_manual.py` - add the page to the `docs` constant
|
* `bin/make_manual.py` - add the page to the `docs` constant
|
||||||
|
|
||||||
|
## Writing a plugin ##
|
||||||
|
|
||||||
|
New features (backends, commands) can also be added "out-of-tree", through Go plugins.
|
||||||
|
Changes will be kept in a dynamically loaded file instead of being compiled into the main binary.
|
||||||
|
This is useful if you can't merge your changes upstream or don't want to maintain a fork of rclone.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
|
||||||
|
- Naming
|
||||||
|
- Plugins names must have the pattern `librcloneplugin_KIND_NAME.so`.
|
||||||
|
- `KIND` should be one of `backend`, `command` or `bundle`.
|
||||||
|
- Example: A plugin with backend support for PiFS would be called
|
||||||
|
`librcloneplugin_backend_pifs.so`.
|
||||||
|
- Loading
|
||||||
|
- Supported on macOS & Linux as of now. ([Go issue for Windows support](https://github.com/golang/go/issues/19282))
|
||||||
|
- Supported on rclone v1.50 or greater.
|
||||||
|
- All plugins in the folder specified by variable `$RCLONE_PLUGIN_PATH` are loaded.
|
||||||
|
- If this variable doesn't exist, plugin support is disabled.
|
||||||
|
- Plugins must be compiled against the exact version of rclone to work.
|
||||||
|
(The rclone used during building the plugin must be the same as the source of rclone)
|
||||||
|
|
||||||
|
Building
|
||||||
|
|
||||||
|
To turn your existing additions into a Go plugin, move them to an external repository
|
||||||
|
and change the top-level package name to `main`.
|
||||||
|
|
||||||
|
Check `rclone --version` and make sure that the plugin's rclone dependency and host Go version match.
|
||||||
|
|
||||||
|
Then, run `go build -buildmode=plugin -o PLUGIN_NAME.so .` to build the plugin.
|
||||||
|
|
||||||
|
[Go reference](https://godoc.org/github.com/rclone/rclone/lib/plugin)
|
||||||
|
|
||||||
|
[Minimal example](https://gist.github.com/terorie/21b517ee347828e899e1913efc1d684f)
|
||||||
|
|
16
lib/plugin/package.go
Normal file
16
lib/plugin/package.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Package plugin implements loading out-of-tree storage backends
|
||||||
|
// using https://golang.org/pkg/plugin/ on Linux and macOS.
|
||||||
|
//
|
||||||
|
// If the $RCLONE_PLUGIN_PATH is present, any Go plugins in that dir
|
||||||
|
// named like librcloneplugin_NAME.so will be loaded.
|
||||||
|
//
|
||||||
|
// To create a plugin, write the backend package like it was in-tree
|
||||||
|
// but set the package name to "main". Then, build the plugin with
|
||||||
|
//
|
||||||
|
// go build -buildmode=plugin -o librcloneplugin_NAME.so
|
||||||
|
//
|
||||||
|
// where NAME equals the plugin's fs.RegInfo.Name.
|
||||||
|
package plugin
|
||||||
|
|
||||||
|
// Build for plugin for unsupported platforms to stop go complaining
|
||||||
|
// about "no buildable Go source files".
|
41
lib/plugin/plugin.go
Normal file
41
lib/plugin/plugin.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// +build darwin linux
|
||||||
|
|
||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"plugin"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dir := os.Getenv("RCLONE_PLUGIN_PATH")
|
||||||
|
if dir == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Get file names of plugin dir
|
||||||
|
listing, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "Failed to open plugin directory:", err)
|
||||||
|
}
|
||||||
|
// Enumerate file names, load valid plugins
|
||||||
|
for _, file := range listing {
|
||||||
|
// Match name
|
||||||
|
fileName := file.Name()
|
||||||
|
if !strings.HasPrefix(fileName, "librcloneplugin_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(fileName, ".so") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Try to load plugin
|
||||||
|
_, err := plugin.Open(filepath.Join(dir, fileName))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to load plugin %s: %s\n",
|
||||||
|
fileName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import (
|
||||||
_ "github.com/rclone/rclone/backend/all" // import all backends
|
_ "github.com/rclone/rclone/backend/all" // import all backends
|
||||||
"github.com/rclone/rclone/cmd"
|
"github.com/rclone/rclone/cmd"
|
||||||
_ "github.com/rclone/rclone/cmd/all" // import all commands
|
_ "github.com/rclone/rclone/cmd/all" // import all commands
|
||||||
|
_ "github.com/rclone/rclone/lib/plugin" // import plugins
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user