SBT Dynamic Project Dependency - sbt

I recently need to integrate some projects as submodules, and each of them has its own build.sbt.
And there are also dependencies between submodules.
Therefore, I need to dynamically convert the libraryDependencies to dependsOn format.
The project structure looks like this, and root depends on A and B, while A also depends on B.
.
├── build.sbt
├── projA
│   ├── build.sbt
│   └── src
├── projB
│   ├── build.sbt
│   └── src
└── src
   └── main
Since it seems appends .setting/.denpensOn directly to a project that already has build.sbt is ineffective.
// ./build.sbt
lazy val root = (project in file("."))
.settings(commonSettings)
.dependsOn(A, B)
lazy val A = (project in file("projA"))
.settings(commonSettings) <-- not work
.dependsOn(B) <-- not work
lazy val B = (project in file("projB"))
.settings(commonSettings) <-- not work
So, I try this method to dynamically edit libraryDependencies, but this approach is only able to modify settings.
The inter-project dependencies seem to be not controlled by the settings.
Is there any way to dynamically add dependsOn to the project?
Or how to make the depensOn appended on the subproject in the root build.sbt works?
Thanks.

Related

“Controller does not exist. Reflection failed.” TYPO3 v2

I have a typo3 extension (created with extension manager) and it seems no matter what I try I always get the following error:
Class CM\Parser\Controller\ParserController does not exist. Reflection failed.
I used the explanations for this problem TYPO3 tutorial extension, controller does not exist and "Controller does not exist. Reflection failed." TYPO3. Neither of them seem to work.
My composer.json in the root directory has the following entry:
"autoload": {
"psr-4": {
"CM\\parser\\": "./packages/cm-parser/Classes"
}
}
My typo3conf/ext folder has a symlink on packages/cm-parser. My composer.json inside the extension directory (packages/cm-parser) has the entry:
"autoload": {
"psr-4": {
"CM\\parser\\": "./Classes"
}
}
Thanks in advance for any help.
My directory structure looks like this (starting in /opt/lampp/htdocs/my-new-project) which is a typo3 v9.5 installation
> .
├── packages
│   └── cm-parser
│   ├── Classes
│   ├── Configuration
│   ├── Documentation.tmpl
│   ├── Resources
│   └── Tests
├── public
│   ├── fileadmin
│   │   ├── _processed_
│   │   ├── _temp_
│   │   └── user_upload
│   ├── typo3
│   │   └── sysext
│   ├── typo3conf
│   │   ├── ext
│   │   └── l10n
│   ├── typo3temp
│   │   ├── assets
│   │   └── var
│   └── uploads
│   └── tx_extensionbuilder
├── var
...
In my typo3conf/ext directory there is a symlink called parser to packages/cm-parser (I think the composer created that for me).
So I hope this symlink works for Typo3.
The files ext_emconf.php and ext_localconf.php are also in the right place. The folder structure above only displays my folders (tree -L 3) up to the third level.
The controller class is CM\Parser\Controller\ParserController, while in your composer.json you're using CM\\parser\\ (with a lowercase p) in the PSR4 autoload. This should be CM\\Parser\\
After changing this you need to of course run composer dumpautoload to reload the autoload information.
In your root composer.json file:
➊ You do not need the PSR-4 autoload section for "CM\\parser\\".
➋ You possibly have to add the path to packages/* as a repository.
➌ You have to include the composer namespace of your extension.
In your file system:
➍ You do not need typo3conf/ext/ as a symbolic link to packages/.
Try the following changes:
In your root composer.json file, remove the PSR-4 autoload section as outlined above. Add the packages/ directory as a path under repositories. For example:
{
"repositories": [
{
"type": "composer",
"url": "https://composer.typo3.org/"
},
{
"type": "path",
"url": "packages/*"
}
],
...
}
Store your extension code in the following path: packages/parser/.
Assuming your extension key reads parser and your vendor name is CM, the composer namespace becomes cm/parser. Add this as a requirement to the composer config file. You can use the following command on the command line:
composer require cm/parser:dev-master
This assumes, that packages/parser/ is a valid Git repository and has the master branch (do not use a version in the extension's composer.json file).
If the local Git repository and version (in the example above: dev-master) can be found, composer will automatically install all dependencies as required and it will create a symbolic link:
typo3conf/ext/parser -> ../../../packages/parser/
Also double check if all PHP files show the correct PHP namespace: CM\Parser\... and your controller class name reads ParserController.
If you can share your TYPO3 extension code, upload it to GitHub (or any other place) and share the link here. This way people can review your code and possibly spot further errors.

Is it possible to define packages within sbt's project directory

I defined some classes in sbt's project directory using no package (i.e. all my files were directly under project and they did not include any package statement). It worked fine.
Now when I tried to group them into packages and ran sbt reload I got not found: value XXX at the line I imported the package in my build.sbt (XXX is the name of the package).
Can't project deal with packages?
EDIT after comment
It will work if you add your source files in folder project/src/main/scala
Check this structure
tree
.
├── build.sbt
└── project
├── build.properties
└── src
└── main
└── scala
└── foo
└── Bar.scala
5 directories, 3 files
build.sbt
import foo._
version := Bar.ver
and Bar.scala
package foo
object Bar {
val ver = "1.0.0"
}

Can not import Dependencies.scala from top level sbt in multiproject

I have a commons project in sbt which needs some values stored in a Dependencies.scala.
.
├── build.sbt
├── project
│   └── Dependencies.scala
└── src
└── main
└── scala
commons/build.sbt
import Dependencies._
lazy val commons = (project in file("."))
.settings(
name := s"$msg commonsproject",
version := "1.0",
scalaVersion := "2.12.2"
)
commons/project/Dependencies.scala
object Dependencies {
val msg = "Hello world"
}
When I run sbt, I get the correct value for name
[info] Loading project definition from /mydata/workspace/multiproject/commons/project
[info] Set current project to Hello world commonsproject (in build file:/mydata/workspace/multiproject/commons/)
> name
[info] Hello world commonsproject
>
Now, I have integrated it in a top level sbt project.
├── build.sbt(top level)
├── commons
│   ├── build.sbt
│   ├── project
│   │   └── Dependencies.scala
│   └── src
│   └── main
├── project
│   └── build.properties
└── src
├── main
│   └── scala
└── test
└── scala
build.sbt
lazy val commons = (project in file("commons"))
.settings(
name := "subproject",
version := "1.0",
scalaVersion := "2.12.2"
)
lazy val root = (project in file("."))
.settings(
name := "rootproject",
version := "1.0",
scalaVersion := "2.12.2"
).aggregate(commons)
When I run sbt from the top level, it fails with the following error:
/mydata/workspace/multiproject/commons/build.sbt:1: error: not found: object Dependencies
import Dependencies._
^
/mydata/workspace/multiproject/commons/build.sbt:6: error: not found: value msg
name := s"$msg commonsproject",
^
sbt.compiler.EvalException: Type error in expression
[error] sbt.compiler.EvalException: Type error in expression
[error] Use 'last' for the full log.</pre>
Can anyone tell if it's possible to keep the Dependencies.scala in the common project and still be able to run it from a top level sbt ?
The goal would be to be able to run sbt either at the top level or in the subproject while keeping despendencies in each project.
Other design to achieve what I want is also welcome
Thank you
Here is the sample project https://github.com/ccheneson/multiproject
From the SBT 0.13 docs:
You cannot have a project subdirectory or project/*.scala files in the sub-projects. foo/project/Build.scala would be ignored.
In this case your Dependencies.scala file is being ignored because commons is a sub-project - that's why you can't access it.

Sinatra asset pipeline, can't make it work

I am using Sprockets with Sinatra, as suggested in Sinatra's page docs, but I can't make it work.
When I go to localhost:4567, the page loads correctly but with no styles. If I go to localhost:4567/assets/app.css, I get a not found error. I wonder what I am missing or what is wrong in the way I am using Sprockets?
This is my folder structure:
├── assets
│   ├── css
│   │   ├── app.css
│   │   ├── base.css
│   │   └── normalize.css
├── bin
│   └── app
├── lib
│   ├── app_assets.rb
│   └── main.rb
├── spec
│   ├── spec_helper.rb
│   └── main_spec.rb
├── views
│   └── index.erb
├── Gemfile
├── Gemfile.lock
├── Rakefile
├── .rspec
└── .ruby-version
The contents of app.css are:
//= require normalize
//= require base
The contents of app_assets.rb are:
module AppAssets
def self.environment root_path
environment = Sprockets::Environment.new root_path
environment.append_path './assets/css/'
environment
# get assets
get '/assets/*' do
env['PATH_INFO'].sub!('/assets', '')
settings.environment.call(env)
end
end
end
The contents of lib/main.rb are:
require 'sinatra'
require 'sprockets'
require 'app_assets'
class Main < Sinatra::Base
set :views, "#{settings.root}/../views"
get '/' do
erb :index
end
end
The file views/index.erb contains the line:
<link rel="stylesheet" href="assets/app.css">
And the contents of bin/app are:
#!/usr/bin/env ruby
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
require 'sinatra'
require 'sprockets'
require 'app_assets'
require 'main'
Main.run!
Which I run typing:
$ bin/app
Any help would be appreciated, I'm sure I made something wrong but I can't see what. Can anybody spot it?
The app_assets.rb file is the problem here. When you require this file inside another file, the methods you define inside this module are not automatically included. You need to explicitly include AppAssets wherever you need the self.environment method to exist.
The second issue here is that self.environment is not equivalent to settings.environment. If I understand correctly, what you're trying to do is define the asset routing whenever the module gets included. To achieve this one way is to use the included hook for modules. This hook gets run every time you include a module inside a context. If you use that, the code in app_assets.rb turns to:
module AppAssets
def self.included(klass)
environment = Sprockets::Environment.new klass.settings.root
# note the change to path. Since the file where this gets included
# is inside a sub-folder, we need to traverse to one level above.
environment.append_path '../assets/css/'
klass.set :environment, environment
klass.get '/assets/*' do
env['PATH_INFO'].sub!('/assets', '')
klass.settings.environment.call(env)
end
end
end
The klass argument to this hook is the class into which this module is included. In our case this is the Sinatra class you've described in main.rb. That file looks like:
class Main < Sinatra::Base
include AppAssets
# Same as what you have
end
There's a Sinatra Recipes article about using Sprockets with Sinatra: http://recipes.sinatrarb.com/p/asset_management/sprockets?#article

Are there any pros to add root module in build.sbt?

I have multi module sbt project:
├── build.sbt
├── bar
│   ├── build.sbt
│   └── ...
├── foo
│   ├── build.sbt
│   └── ...
└── ...
And 2 versions of build.sbt:
lazy val foo = project in(file("./foo"))
lazy val bar = project in(file("./bar"))
And second version:
lazy val foo = project in(file("./foo"))
lazy val bar = project in(file("./bar"))
lazy val root = Project(id = "root",
base = file(".")) aggregate(foo, bar)
What are the differences between these versions? Are there any pros in second version?
With your second version loaded type projects at the sbt command line and you should see that you have three of them. The one with a star at the front is the default project. It will be your root project. Commands that you type will apply to it. As it is an aggregate project the commands will be applying in turn to both foo and bar.
package is a good example command - typing it should produce a jar file that has both the foo and bar classes in it, if that is what you want.

Resources