Dependency not resolved correctly when Product (CodeGenerator) used in Rule ([asd] -> [cpp]) - qbs

when using a Rule inside a Module with a Dependency to a Product, a FileTagger breaks the resolution of dependencies in qbs.
We have a CodeGenerator in our project which is build by the project itself.
This CodeGenerator generates C++-Classes from *.asd-Files.
A Product called "Core" uses that CodeGenerator to generate Classes from Core.asd Files.
I am not sure if this is a bug in qbs, but since qbs 1.8 this part of our project does not work anymore.
I created a small test project that illustrates that problem:
RuleUsesProduct.qbs
import qbs
Project {
minimumQbsVersion: "1.8.0"
references: [
"Core/Core.qbs",
"CodeGenerator/CodeGenerator.qbs"
]
qbsSearchPaths: "QBS"
}
Core.qbs
import qbs
CppApplication {
Depends { name: "Qt.core" }
cpp.cxxLanguageVersion: "c++11"
cpp.defines: [
]
consoleApplication: true
files: [
"main.cpp",
"core.asd"
]
Depends{ name:"CodeGenerator"}
Depends{ name:"CodeGeneration"}
Group { // Properties for the produced executable
fileTagsFilter: product.type
qbs.install: true
}
}
Db2cppModule.qbs
This is indirectly included via the qbsSearchPath from RuleUsesProductTest.qbs
import qbs 1.0
import qbs.Environment
import qbs.FileInfo
import qbs.TextFile
import qbs.Process
import qbs.File
Module {
FileTagger {
patterns: ["*.asd"]
fileTags: ["asd"]
}
Rule {
id: dbRule
inputs: ["asd"]
inputsFromDependencies: ["application"]
multiplex: true
outputFileTags: ["cpp", "hpp"]
outputArtifacts: {
// dummy code that should call the CodeGenerator.exe with some parameters...
var process = new Process();
console.warn("# " + inputs["application"][0].filePath)
var cmdExp = "" + inputs["application"][0].filePath;
process.exec(cmdExp, [], false);
return []
}
prepare: {
console.warn("*" + inputs["application"][0].filePath)
var cmdExp = "" + inputs["application"][0].filePath;
var cmd = new Command(cmdExp, []);
return cmd
}
}
}
CodeGenerator.qbs
import qbs
CppApplication {
Depends { name: "Qt.core" }
cpp.cxxLanguageVersion: "c++11"
cpp.defines: [
]
consoleApplication: true
files: [
"codegenerator.cpp"
]
Group { // Properties for the produced executable
fileTagsFilter: product.type
qbs.install: true
}
}
Any help is much appreciated!

Qbs knows two types of rules: Multiplex and non-multiplex ones. Details can be found here: https://doc.qt.io/qbs/rule-item.html.
The important point for you is that non-multiplex rules invoke their prepare script once for every input. This means that in your example, the prepare script runs twice: Once for the asd input and once for the application input. The respective other inputs are not visible. This does not fit your use case, because you want to see both inputs at the same time. Therefore, you need to make your rule a multiplex rule:
multiplex: true
In the prepare script, you need to create one command for every input of type asd (unless your generator can take several inputs at once).
Rule of thumb (no pun intended): If your rule declares more than one input tag, it probably should be a multiplex rule.

Related

Eclipse editor keeps hanging when editing Xtend files ( 'updating editor state (sleeping)' )

Recently, my Eclipse editor started becoming really sluggish when editing Xtend files. I am using Xtend in the context of the Xtext framework (which provides certain interfaces using Xtend). I thought it had something to do with my Eclipse installation, so I deleted it entirely off my MacBook and re-installed the newest version (Eclipse 2019-09). However, it was still really sluggish when editing Xtend files. It does this every few seconds (loading figure appears, it hangs for a few seconds after which it repeats this sequence). I notice that the Progress tab in my Eclipse editor shows the message 'updating editor state (sleeping)' after it stops hanging. It only happens with Xtend files (other files like java work just fine in Eclipse without hanging). The Xtend file I am working in is currently about 500 Lines of Codes. Not a small file, but could this be the reason my Eclipse IDE keeps hanging? Things I already tried to fix this hanging problem:
Fully delete and re-install Eclipse on my MacBook (only installing the necessary plug-ins that I need: The Palladio PCM framework, and the Xtext Framework).
Provide extra memory (up to 6000 MB) for the Eclipse environment and its VMs (using the memory parameters (XmX e.g.) in the eclipse.ini file).
Suspend all validators in the Eclipse environment (disabling the option to override this).
The weird thing is that it keeps telling 'updating editor state (sleeping)' after it stops hanging. This message appears for about 0.1 to 2 seconds. I think that disabling the 'editor state update' might resolve this problem, but I don't know where I can suspend this option. Does anyone have a clue on why my Eclipse IDE keeps hanging every few seconds when editing Xtend files?
EDIT 9 December 2019:
/*
* generated by Xtext 2.17.1
*/
package org.xtext.example.mydsl.generator
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.generator.AbstractGenerator
import org.eclipse.xtext.generator.IFileSystemAccess2
import org.eclipse.xtext.generator.IGeneratorContext
import org.xtext.example.mydsl.finalDsl.UserProfile
import org.xtext.example.mydsl.finalDsl.Model
import org.xtext.example.mydsl.finalDsl.ConceptualMethod
import org.xtext.example.mydsl.finalDsl.Expression
import org.xtext.example.mydsl.finalDsl.AdditionalExpressions
import org.xtext.example.mydsl.finalDsl.RelationalOperator
import org.xtext.example.mydsl.finalDsl.SingleLibraryBusinessMethodStatement
import org.xtext.example.mydsl.finalDsl.SingleLibraryPersistenceMethodStatement
import org.xtext.example.mydsl.finalDsl.SingleLibraryInterFaceMethodStatement
import org.xtext.example.mydsl.finalDsl.IfStatements
import org.xtext.example.mydsl.finalDsl.IfElseStatements
import org.eclipse.emf.ecore.EcoreFactory
import org.eclipse.emf.ecore.EPackage
import org.eclipse.xtext.ISetup
import org.eclipse.emf.ecore.EStructuralFeature
import java.util.TreeMap
import java.util.HashMap
import java.util.Set
import org.palladiosimulator.pcm.util.*
import org.palladiosimulator.pcm.repository.impl.*
import org.palladiosimulator.pcm.repository.BasicComponent
import org.eclipse.emf.ecore.EClass
import org.palladiosimulator.pcm.core.CoreFactory
import org.palladiosimulator.pcm.repository.RepositoryFactory
import org.palladiosimulator.pcm.seff.SeffFactory
import org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl
import org.eclipse.emf.common.util.URI
import com.google.inject.Injector
import org.xtext.example.mydsl.FinalDslStandaloneSetup
import org.eclipse.emf.ecore.resource.ResourceSet
import com.google.inject.Injector;
import org.eclipse.emf.ecore.util.EcoreUtil
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl
import org.palladiosimulator.pcm.seff.seff_performance.ParametricResourceDemand
import org.palladiosimulator.pcm.seff.seff_performance.impl.ParametricResourceDemandImpl
import org.palladiosimulator.pcm.seff.seff_performance.SeffPerformanceFactory
import java.util.Collections
import org.eclipse.emf.ecore.EObject
import org.xtext.example.mydsl.finalDsl.ElseStatement
import java.util.ArrayList
import java.math.BigDecimal
import org.xtext.example.mydsl.finalDsl.Statement
import org.xtext.example.mydsl.finalDsl.RelationalOperator
import org.xtext.example.mydsl.finalDsl.AdditionOperator
import org.xtext.example.mydsl.finalDsl.MultiplicationOperator
import org.xtext.example.mydsl.finalDsl.libraryInterFaceMethodStatementEnum
/**
* Generates code from your model files on save.
*
* See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation
*/
class FinalDslGenerator extends AbstractGenerator {
override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context ) {
// Method call to build and store PCM Palladio repository. Right click on the repository file
// to create a .repository_diagram file to 'graphically' view and edit the repository accordingly.
RepositoryBuilder(resource,fsa)
// Method call to calculate the resource demands for the If/Else statements in the model according
// to the specified probabilities in the user profiles.
UserProfileEstimator(resource,fsa)
// Method call to build and store your modeled DSL instance accordingly. Mind that this has nothing
// to do with the creation of the PCM Palladio repository above, but it might come in handy if you want
// to export your DSL instances (for use outside of the xText IDE environment).
ModelXMLBuilder(resource)
}
// Method for calculating the resource demands for the If/Else statements in the model according to the
// specified probabilities in the user profiles. The declared probabilities are mapped to the If/Else Statements
// by using a set of for loops (see loops for elaboration documentation).
def UserProfileEstimator (Resource resource,IFileSystemAccess2 fsa) {
// Treemap of <String,Integer> pairs describing the set of library functions and their resource demands. These
// resource demands can later be substituted for resource demand sample data in order to calculate the resource
// demand estimations for the performance models.
var TreeMap<String,Integer> resourceTable = new TreeMap
resourceTable.put("Hash",120)
resourceTable.put("Concatenate",1000)
resourceTable.put("Average",60)
resourceTable.put("Count",80)
resourceTable.put("Today",80)
resourceTable.put("Time",80)
resourceTable.put("Random",80)
resourceTable.put("Max",43)
resourceTable.put("Min",45)
resourceTable.put("Root",80)
resourceTable.put("Square",80)
resourceTable.put("ReadDatabase",43)
resourceTable.put("Message",123)
resourceTable.put("ErrorMessage",100)
resourceTable.put("WarningMessage",43)
resourceTable.put("WriteDatabase",78)
resourceTable.put("DeleteFromDatabase",67)
resourceTable.put("Log",40)
resourceTable.put('>',40)
resourceTable.put('<>',40)
resourceTable.put('<',40)
resourceTable.put('=>',40)
resourceTable.put('<=',40)
resourceTable.put('==',40)
resourceTable.put('in',40)
resourceTable.put("+",40)
resourceTable.put("-",40)
resourceTable.put("OR",40)
resourceTable.put("*",40)
resourceTable.put("/",40)
resourceTable.put("AND",40)
// check if statements
var conceptualMethodDemandList = newArrayList
var ifElseDemandList = newArrayList
// TreeMap variable in order to store all the user profiles (indicated by the String variable in the list
// and the corresponding resource demands (stored as Arraylist <String> arrays). The strings in this latter
// Arraylist represent the probabilities for the If/Else statements in the model. Each of these probabilities
// Contains the resource demand for the condition evaluation of the If-Statement, the If Statement body itself
// and the Else statement. If one of these parts contains no Resource demand (if no library functions are present)
// if an Else statement is not defined, the Resource Demand is zero.
var TreeMap <String, ArrayList <String> > completeDemandList = new TreeMap
// For loop to traverse through all declared user profiles in the model.
for (userprofile : resource.allContents.toIterable.filter(UserProfile)) {
// For loop to traverse through all declared Conceptual Methods in the model. A conceptual method counter
// variable is stored in order to keep track of which Conceptual Method is processed by the for loop. This
// counter is used as the index for processing all user profile and resource demand data which are both stored
// as arrays.
var conceptualMethodCounter = 0
for (conceptualMethod : resource.allContents.toIterable.filter(ConceptualMethod)) {
// For loop traversing through all If/Else statements in the corresponding conceptual methods declared in the model.
// An ifElseStatementCounter variable is stored for the same reason as specified in the Conceptual Method for loop
// (see documentation above Conceptual Method for loop).
var ifElseStatementCounter = 0
for (ifElseStatement : conceptualMethod.eAllContents.toIterable.filter(IfElseStatements)) {
var ifDemand = 0
var elseDemand = 0
var expressionDemand = 0
// In the for-loop each if/Else statement is filtered for its If Statement part. This if Statement part is then filtered for
// all three Library Function sorts. This happens for the functions in the expression part of the If Statement (containing the
// condition that is evaluated), the actual If Statement part, and for the Else Statement part (in the case
// an else statement is present in the If/Else statment).
// The dispatch method getDemand method is called for each library function statement together with the
// resourceTable variable containing all library functions and their resource demands. This dispatch method returns
// the resource demand for the respective library function, after which this result is added to a 'Demand' variable
// which contains the aggregate sum of all library functions for the calculated If/Else Statement part.
for (ifStatement : ifElseStatement.eAllContents.toIterable.filter(IfStatements)){
for (expressionResourceStatement : ifStatement.expression.eAllContents.toIterable) {
switch(expressionResourceStatement){
SingleLibraryPersistenceMethodStatement,
SingleLibraryInterFaceMethodStatement,
SingleLibraryBusinessMethodStatement,
RelationalOperator,
AdditionOperator,
MultiplicationOperator:
expressionDemand += getDemand(expressionResourceStatement,resourceTable)
}
}
for (innerIfStatements : ifStatement.statements){
for (ifResourceStatement : innerIfStatements.eAllContents.toIterable) {
switch(ifResourceStatement){
SingleLibraryPersistenceMethodStatement,
SingleLibraryInterFaceMethodStatement,
SingleLibraryBusinessMethodStatement,
RelationalOperator,
AdditionOperator,
MultiplicationOperator:
ifDemand += getDemand(ifResourceStatement,resourceTable)
}
}
}
for (elseStatement : ifElseStatement.eAllContents.toIterable.filter(ElseStatement)){
for (elseResourceStatement : elseStatement.eAllContents.toIterable) {
switch(elseResourceStatement){
SingleLibraryPersistenceMethodStatement,
SingleLibraryInterFaceMethodStatement,
SingleLibraryBusinessMethodStatement,
RelationalOperator,
AdditionOperator,
MultiplicationOperator:
elseDemand += getDemand(elseResourceStatement,resourceTable)
}
}
}
// end of IfStatements for loop
}
// Add the resource demands of the evaluationDemand variable to the ifElseDemandList variable containing the resource demands
// for the If/Else statement that is currently processed in the for-loop.
ifElseDemandList.add("EvaluationDemands" + (conceptualMethodCounter+1).toString + "." + (ifElseStatementCounter+1).toString + ": " + BigDecimal.valueOf(expressionDemand))
// Add the resource demands of the ifDemand and elseDemand variable to the ifElseDemandList variable containing the resource demands
// for the If/Else statement that is currently processed in the for-loop. In the case the probability is declared in the user profile
// as zero (.0), the probability is assumed to be .50. The second if-statement below filters and handles these cases accordingly.
if (userprofile.probabilitysets.get(conceptualMethodCounter).probability.get(ifElseStatementCounter) != 0) {
ifElseDemandList.add("IfStatementDemands" + (conceptualMethodCounter+1).toString + "." + (ifElseStatementCounter+1).toString + ": " + BigDecimal.valueOf(ifDemand*userprofile.probabilitysets.get(conceptualMethodCounter).probability.get(ifElseStatementCounter)))
ifElseDemandList.add("ElseStatementDemands" + (conceptualMethodCounter+1).toString + "." + (ifElseStatementCounter+1).toString + ": " + BigDecimal.valueOf(elseDemand*(1-userprofile.probabilitysets.get(conceptualMethodCounter).probability.get(ifElseStatementCounter))))
}
if (userprofile.probabilitysets.get(conceptualMethodCounter).probability.get(ifElseStatementCounter) == 0) {
ifElseDemandList.add("IfStatementDemands" + (conceptualMethodCounter+1).toString + "." + (ifElseStatementCounter+1).toString + ": " + BigDecimal.valueOf(ifDemand*0.5))
ifElseDemandList.add("ElseStatementDemands" + (conceptualMethodCounter+1).toString + "." + (ifElseStatementCounter+1).toString + ": " + BigDecimal.valueOf(elseDemand*0.5))
}
// Once all the parts of the If/Else statement in the for-loop are processed, the resulting list of resource demands for the If/Else statement (stored
// in the ifElseDemandList variable) are added to the conceptualMethodDemandList variable containing the resource demands for all
// If/Else statements in the conceptual method. Next the ifElseDemandList is cleared and the ifElseStatementCounter is incremented in order for the next for-loop
// iteration to process the next If/Else statement of the conceptual method.
conceptualMethodDemandList.add('''
«ifElseDemandList.clone.toString»
''')
ifElseDemandList.clear
ifElseStatementCounter++
}
// Once all the If/Else statements in the for-loop are processed, the resulting list of resource demands for all If/Else statements (stored
// in the conceptualMethodDemandList variable) are added to the completeDemandList variable containing the resource demands for all
// conceptual methods and user profiles.
conceptualMethodCounter++
completeDemandList.put('''
«userprofile.name»
''',conceptualMethodDemandList.clone() as ArrayList<String>)
}
conceptualMethodDemandList.clear
}
// The completeDemandList variable containing all resource demands according to all the specified user profiles, are
// stored below in a text file.
fsa.generateFile(
"IfElseStatementsDemands.txt",
completeDemandList.toString)
}
// Dispatch method for looking up the resource demand of a library business function in a TreeMap variable
// containing the resource demands for all library functions.
def dispatch getDemand (SingleLibraryBusinessMethodStatement statement, TreeMap<String,Integer> resourceTable) {
return resourceTable.get(statement.libraryFunction.toString)
}
// Dispatch method for looking up the resource demand of a library interface function in a TreeMap variable
// containing the resource demands for all library functions.
def dispatch getDemand (SingleLibraryInterFaceMethodStatement statement, TreeMap<String,Integer> resourceTable) {
return resourceTable.get(statement.libraryFunction.toString)
}
// Dispatch method for looking up the resource demand of a library persistence function in a TreeMap variable
// containing the resource demands for all library functions.
def dispatch getDemand (SingleLibraryPersistenceMethodStatement statement, TreeMap<String,Integer> resourceTable) {
return resourceTable.get(statement.libraryFunction.toString)
}
def dispatch getDemand (RelationalOperator statement, TreeMap<String,Integer> resourceTable) {
return resourceTable.get(statement.operator.toString)
}
def dispatch getDemand (AdditionOperator statement, TreeMap<String,Integer> resourceTable) {
return resourceTable.get(statement.operator.toString)
}
def dispatch getDemand (MultiplicationOperator statement, TreeMap<String,Integer> resourceTable) {
return resourceTable.get(statement.operator.toString)
}
def RepositoryBuilder (Resource resource,IFileSystemAccess2 fsa) {
var repository = RepositoryFactory.eINSTANCE.createRepository
repository.setEntityName("Repository for Functional Model")
var myInterface = RepositoryFactory.eINSTANCE.createOperationInterface()
myInterface.setEntityName("My Interface")
repository.getInterfaces__Repository().add(myInterface); //add first class entity
var opProvRole = RepositoryFactory.eINSTANCE.createOperationProvidedRole()
opProvRole.setEntityName("Provided Role of Basic Component")
// set the interface for the role:
opProvRole.setProvidedInterface__OperationProvidedRole(myInterface)
for (element : resource.allContents.toIterable.filter(ConceptualMethod))
{
var component = RepositoryFactory.eINSTANCE.createBasicComponent()
component.setEntityName(element.name)
ConceptualClass2SEFF(component, element)
// set/add the role for basic component:
component.getProvidedRoles_InterfaceProvidingEntity().add(opProvRole)
repository.getComponents__Repository().add(component) //add first class entity
}
var storeRepositoryURI = URI.createURI(resource.URI.trimSegments(1).toString + "/src-gen/TestRepository.repository")
var result = new XMLResourceImpl(storeRepositoryURI)
result.getContents().add(repository)
result.save(null)
}
def ConceptualClass2SEFF (BasicComponent component, ConceptualMethod conceptualmethod) {
for (statement : conceptualmethod.eAllContents.toIterable.filter(SingleLibraryPersistenceMethodStatement))
{
var seff = SeffFactory.eINSTANCE.createResourceDemandingSEFF()
var internalAction = SeffFactory.eINSTANCE.createInternalAction()
internalAction.entityName = statement.libraryFunction.toString
internalAction.resourceDemand_Action.add(SeffPerformanceFactory.eINSTANCE.createParametricResourceDemand)
seff.steps_Behaviour.add(internalAction)
component.serviceEffectSpecifications__BasicComponent.add(seff)
}
for (statement : conceptualmethod.eAllContents.toIterable.filter(SingleLibraryInterFaceMethodStatement))
{
var seff = SeffFactory.eINSTANCE.createResourceDemandingSEFF()
var internalAction = SeffFactory.eINSTANCE.createInternalAction()
internalAction.entityName = statement.libraryFunction.toString
internalAction.resourceDemand_Action.add(SeffPerformanceFactory.eINSTANCE.createParametricResourceDemand)
seff.steps_Behaviour.add(internalAction)
component.serviceEffectSpecifications__BasicComponent.add(seff)
}
for (statement : conceptualmethod.eAllContents.toIterable.filter(SingleLibraryBusinessMethodStatement))
{
var seff = SeffFactory.eINSTANCE.createResourceDemandingSEFF()
var internalAction = SeffFactory.eINSTANCE.createInternalAction()
internalAction.entityName = statement.libraryFunction.toString
internalAction.resourceDemand_Action.add(SeffPerformanceFactory.eINSTANCE.createParametricResourceDemand)
seff.steps_Behaviour.add(internalAction)
component.serviceEffectSpecifications__BasicComponent.add(seff)
}
}
def ModelXMLBuilder (Resource dslInstance) {
var injector = new FinalDslStandaloneSetup().createInjectorAndDoEMFRegistration()
var resourceSet = injector.getInstance(ResourceSet)
dslInstance.load(null)
EcoreUtil.resolveAll(resourceSet)
var storeXmiURI = URI.createURI(dslInstance.URI.trimSegments(1).toString + "/src-gen/DslModel.xmi")
var xmiResource = resourceSet.createResource(storeXmiURI)
xmiResource.getContents().add(dslInstance.getContents().get(0))
xmiResource.save(null)
}
}

Is there a way to trim a TM_FILENAME beyond using TM_FILENAME_BASE?

I am trying to create a snippet for a redux container file that takes an import of some react file of the same base name. TM_FILENAME_BASE works great for removing the .js from the file name but in this case, my component file's extension is fun-thing.component.js and the container will follow suit with an extension that is fun-thing.container.js.
The Regex I am using to select everything before the first period is ^([^.]+)
"Redux Container": {
"prefix": "rc",
"body": [
"// #flow",
"import { connect } from 'react-redux';",
"import { ${TM_FILENAME/^([^.]+)/${1:/pascalcase}/}Component } from './${TM_FILENAME/^([^.]+)/$1/}.component';",
"",
"const mapStateToProps = (state) => ({});",
"",
"const mapDispatchToProps = {};",
"",
"export const ${TM_FILENAME/^([^.]+)/${1:/pascalcase}/} = connect(",
" mapStateToProps,",
" mapDispatchToProps",
")(${TM_FILENAME/^([^.]+)/${1:/pascalcase}/}Component);"
],
"description": "Creates a normal container for a normal component"
}
expected
// #flow
import { connect } from 'react-redux';
import { FunThingComponent } from './fun-thing.component';
...
actual
// #flow
import { connect } from 'react-redux';
import { FunThing.container.jsComponent } from './FunThing.container.js.component';
...
As you can see, it is not omitting the file extensions.
Both of these work:
"import { ${TM_FILENAME/^([^.]+).*/${1:/pascalcase}/}Component } from './${TM_FILENAME/^([^.]+).*/$1/}.component';",
"import { ${TM_FILENAME/(.*?)\\..+/${1:/pascalcase}/}Component } from './${TM_FILENAME/(.*?)\\..+/$1/}.component';"
With the vscode snippet transforms if you want to exclude part of the variable as you want, removing the .component.js from the filename, then that part of the variable must be accounted for in the regex - hence (.*?)\\..+.
Otherwise that "unseen" part of the variable just passes through.
So your regex ^([^.]+) was accurately capturing the part of the filename before the first . but then the rest of the variable was "passing through" unmodified.
You can see this more clearly with this example:
"import { ${TM_FILENAME//${1:/pascalcase}/}Component }
that yields:
import { fun-thing.component.jsComponent }
so the entire filename is passed through although none of it is captured.
${someVariable/everything To Be Transformed/what To Do To the previous/}
if it isn't in the "everything to be transformed" part nothing happens to it.

Why does Jooq code-generation break with PostGIS?

Context - I am trying out Postgres' Geographic Information System extension PostGis that enables stories latitude and longitudes as Point and operations on it.
If I understand correctly then I need to add a custom converter that can convert the point between JOOQ and PostGis and add it to the gradle file.
Problem - When I generate the jooq-code, few files are generated incorrectly and have the fields defined twice which fail compilation. These are:
<configured-generation-dir>/tables/StValuecount.java
<configured-generation-dir>/tables/records/StValuecountRecord.java
<configured-generation-dir>/tables/records/StValuepercentRecord.java
<configured-generation-dir>/tables/_StValuecount.java
<configured-generation-dir>/tables/records/_StValuecountRecord.java
<configured-generation-dir>/tables/_StHistogram.java
<configured-generation-dir>/tables/records/_StHistogramRecord.java
<configured-generation-dir>/tables/_StQuantile.java
Gradle config =>
jooq{
myAwesomeApp(sourceSets.main){
logging = 'WARN'
jdbc {
driver = 'org.postgresql.Driver'
url = db_url
user = db_user
password = db_password
}
generator {
name = 'org.jooq.codegen.DefaultGenerator'
strategy {
name = 'org.jooq.codegen.DefaultGeneratorStrategy'
}
database {
name = 'org.jooq.meta.postgres.PostgresDatabase'
inputSchema = 'public'
forcedTypes {
forcedType {
userType = 'org.postgis.Point'
converter = 'com.example.JooqBreaksWithPostGis.jooq.converters.PostgresPointJooqConverter'
expression = '.*\\.point'
types = '.*'
}
}
}
generate {
routines = false
relations = true
deprecated = false
records = true
immutablePojos = false
fluentSetters = true
}
target {
packageName = 'jooq.fancy.app'
directory = 'src/main/java/generated'
}
}
}
}
What am I doing wrong?
I have also created a minimal project where I have reproduced the problem in case someone wants to quickly try it.
Steps to reproduce
Checkout project
git clone git#github.com:raj-saxena/JooqBreaksWithPostGis.git
Go to the project directory and start postgis docker container with
docker-compose up
Similarly, to remove postgis docker container run
docker-compose down
Run migrations that add a simple City table containing Point type with
./gradlew flywayMigrate
I have added few rows in a second migration to verify if the DB structure was working. Details to connect to Postgres instance in the build.gradle file.
Generate jooq files with
./gradlew generateMyAwesomeAppJooqSchemaSource
Verify that the files are generated in the configured src/main/java/generated directory.
Verify that the files mentioned above fail to compile.
Taking Lukas' advice, I added the exclude configuration to the jooq config as below:
database {
name = 'org.jooq.meta.postgres.PostgresDatabase'
...
excludes = '.*ST_ValueCount' +
'|.*St_Valuepercent' +
'|.*St_Histogram' +
'|.*St_Quantile' +
'|.*St_Approxhistogram' +
'|.*St_PixelOfValue' +
'|.*St_Approxquantile' +
'|.*ST_Tile'
}
This allowed the code to compile.
This sounds a lot like https://github.com/jOOQ/jOOQ/issues/4055. jOOQ 3.11 currently cannot handle overloaded table valued functions in any RDBMS that supports table valued functions. Your best option here is to exclude all the affected functions from the code generation, using <excludes>:
https://www.jooq.org/doc/latest/manual/code-generation/codegen-advanced/codegen-config-database/codegen-database-includes-excludes/

Gulp: Passing multiple command line parameters to the same task

Goal:
Build /src/assets/(theme)/*.scss by passing the parameter in the command line using flag --theme. I solved step one using gulp-utils
function sass() {
// return gulp.src('/src/assets/scss/**/*.scss')
return gulp.src(['src/assets/scss/' + (util.env.theme ? util.env.theme : 'app') + '.scss'])
.pipe($.sourcemaps.init())
.pipe($.sass({
includePaths: PATHS.sass
})
.on('error', $.sass.logError))
.pipe($.autoprefixer({
browsers: COMPATIBILITY
}))
// Comment in the pipe below to run UnCSS in production
//.pipe($.if(PRODUCTION, $.uncss(UNCSS_OPTIONS)))
.pipe($.if(PRODUCTION, $.cleanCss({ compatibility: 'ie9' })))
.pipe($.if(!PRODUCTION, $.sourcemaps.write()))
.pipe(gulp.dest(PATHS.dist + '/assets/css'))
.pipe(browser.reload({ stream: true }));
}
Remaining:
gulp build --production --theme folderName1, folderName2, folderName*
Passing multiple theme values --theme folderName1 folderName2 to the same sass() function/task.
This would send folderName to function sass()
function sass() {
// return gulp.src('/src/assets/scss/**/*.scss')
return gulp.src([
'src/assets/scss/folderName1/*.scss',
'src/assets/scss/folderName2/*.scss',
'src/assets/scss/folderName3/*.scss'
])
...
...
}
Would creating an argsList like this SitePoint article discusses work?
Please review your idea. This package looks like solve similar topic
https://www.npmjs.com/package/gulp-sass-themes
You need only group your themes in sub-folder.
From other side gulp.src accepts string and array so you can pass this information from command line.
https://github.com/gulpjs/gulp/blob/master/docs/API.md
One of my colleagues was able to help resolve this issue. Instead of using gulp.utils he was able to use yargs.argv.theme with slipt(,). This allows for a user to pass the --theme param and pass as many comma-separated items as desired.
function sass() {
if (yargs.argv.theme) {
let collection = yargs.argv.theme.split(','),
results = collection.map(item => `./src/assets/scss/${item}/*.scss`);
return gulp.src(results)
.pipe($.sourcemaps.init())
.pipe($.sass({
includePaths: PATHS.sass
})
.on('error', $.sass.logError))
.pipe($.autoprefixer({
browsers: COMPATIBILITY
}))
// Comment in the pipe below to run UnCSS in production
//.pipe($.if(PRODUCTION, $.uncss(UNCSS_OPTIONS)))
.pipe($.if(PRODUCTION, $.cleanCss({ compatibility: 'ie9' })))
.pipe($.if(!PRODUCTION, $.sourcemaps.write()))
.pipe(gulp.dest(PATHS.dist + '/assets/css'))
.pipe(browser.reload({ stream: true }));
}
}

Why doesn't karma-cli accept files as command line argument?

I'm using the config from my project but would like to run karma just for one specific test script one time. I don't want to have to create a whole new config file just for this case and would prefer just passing in the script I want run (so basically telling karma to use files: ['myTest.js'].
But there don't seem to be any options for that AFAICT in the docs. Why would this be missing? It seems like a fundamental feature IMO.
in karma.conf something like that:
function mergeFilesWithArgv(staticFiles) {
var source = staticFiles, argv = process.argv;
argv.forEach(function (arg) {
var index = arg.indexOf('--check=');
if (index !== -1) {
source.push(arg.substring(8));
}
});
return source;
}
config.set({
...
files: mergeFilesWithArgv([
'js_src/tests/*.test.js'
]),
...
});
use: karma start --check='./path/to/file.js'
or for multiple files: karma start --check='./path/to/file.js' --check='/another/path/to/another/file.js'