Perform a uniq in a Array with specific conditions

I think the main problem is the function enableOption.

Because when I perform a test to check the dependencies of a Software when I enable an option, Database changed :dotted_line_face: But how this is possible ??

Add this at the end:

test = SftBase
test.enableOption("Pass1")
puts getRequiredDependencies(test)
puts

Try that with this portion code added, you will see: Carcin

This bug make me crazy to be honest, it tired me in the development of my software

Oh nice, that definitely helps. I’m assuming the problem is that that code outputs an empty array when it should output Sft3 as a dependency?

If so it seems to be that SoftwareInformation#dependencies will return the dependencies of the option if its active and is a pass outright, without also including the @dependencies of the SoftwareInformation instance. Hope that helps.

Finally I did something helpful :joy:

I will explain again, I think I am not clear.

Normally, the first example
https://play.crystal-lang.org/#/r/eoax/
should output all softwares in this order:

#Normally, the expected order should be:

#SftBase Pass1
#Sft1 Pass1
#Sft2 Pass1
#SftBase Pass2
#Sft1 Pass2
#Sft2 Pass2
#Sft3 Pass1
#SftBase Pass3
#Sft1
#Sft2
#Sft3
#SftBase

In the second example,
https://play.crystal-lang.org/#/r/eoe3/edit
The first empty array is normal, it just show when I add this code in the example:

test = SftBase
test.enableOption("Pass1")
puts getRequiredDependencies(test)
puts

The result of the dependencies order is altered.

It’s normal the option “Pass(x)” change the dependencies, it’s a normal behavior.

Because if a software have an option named pass, it mean this software need to be build with this option first. I will don’t explain all changes about that, it will make you more confuse.

Because if you read yet the LFS Book, you will see Gcc for example is build 3 times, but you can’t build three time in a row, you have to build others softwares before (they need to be build few times as well for some)

I did that because I would like to provide a way for user to really build automatically a Linux system from the very beginning

Just to give you an example, this is the script installation of Bash, you will understand (I guess). You can see Pass1 change how to build the software.

class Target < ISM::Software
    
    def configure
        super

        if option("Pass1")
            configureSource([   "--prefix=/usr",
                                "--build=$(support/config.guess)",
                                "--host=#{Ism.settings.target}",
                                "--without-bash-malloc"],
                                buildDirectoryPath)
        else
            configureSource([   "--prefix=/usr",
                                "--docdir=/usr/share/doc/bash-5.1.8",
                                "--without-bash-malloc",
                                "--with-installed-readline"],
                                buildDirectoryPath)
        end
    end
    
    def build
        super

        makeSource([Ism.settings.makeOptions],buildDirectoryPath)
    end
    
    def prepareInstallation
        super

        makeSource([Ism.settings.makeOptions,"DESTDIR=#{builtSoftwareDirectoryPath}#{Ism.settings.rootPath}","install"],buildDirectoryPath)
    end

    def install
        super

        if option("Pass1")
            makeLink("bash","#{Ism.settings.rootPath}bin/sh",:symbolicLink)
        end
    end

end

I hope you can help me to solve this bug, I am very tired of it

Did you try the second code I sent ?

Definitely when I call the function:

test = SftBase
test.enableOption("Pass1")
puts getRequiredDependencies(test)
puts

It change the Database content.

How is it possible ?

Is it a Crystal bug ???

Because I did a simple example, and definitely, when you copy a var and you change it, it affect the first one.

Look this example: https://play.crystal-lang.org/#/r/eoh2

module ISM
 
    class SoftwareOption
 
        property name : String
        property active : Bool
 
    	def initialize(@name = "",@active = false)
        end
 
        def == (other : ISM::SoftwareOption) : Bool
            return @name == other.name && @active == other.active
        end
 
        def isPass : Bool
            return @name.starts_with?(/Pass[0-9]/)
        end
    
    	def enable
      		@active = true
    	end
    
    	def disable
      		@active = false
    	end
 
    end
 
end
 
Data = [ISM::SoftwareOption.new("Monkey",false,)]
 
test = Data[0]
test.enable
 
puts Data[0].active
puts test.active

You will be surprise about the result

Haven’t had a chance to look back into it yet. But looks like you figured out the root issue?

In short no. This is expected behavior. The test variable isn’t getting a copy of the SoftwareOption object from the array, it’s getting a reference to it. So any mutations you apply to test will also be affect the instance in the array since they’re the same, and vice versa. I.e. test.same? Data[0] # => true.

So definitely my bug come from that.

It getting a copy because it’s a constant ? I’m a bit lost now

Because when my program start, it store all SoftwareInformation from json file in the property @softwares of my class CommandLine.

Ism variable contain this class, and this class contain all informations.

require "./RequiredLibraries"

Ism = ISM::CommandLine.new
Ism.start

How should I proceed to get a copy, and not a reference ? I need to dup all ?

Pretty sure it’s because its a class, which is passed by reference. Again there is no “copying” going on. If you still want the reference semantics of a class, but make it so test is NOT the same as Data[0], you can use #dup which is a shallow copy, i.e. only the instance itself is different, it doesn’t change any of the instance vars of that type. There is also the concept of #clone which returns a deep copy in that the instance itself and all its instance vars are also cloned. Another option could be to use a struct, which are passed by value. How to go about it ultimately comes down to what specific semantics you want for you types.

https://play.crystal-lang.org/#/r/eohb

class Foo
  def_clone
end
 
class Bar
  getter foo : Foo
  
  def_clone
  
  def initialize(@foo : Foo);end
end
 
foo = Foo.new
 
bar = Bar.new foo
 
bar2 = bar
 
pp bar2.same? bar # => true
pp foo.same? bar2.foo # => true
 
bar3 = bar.dup
 
pp bar3.same? bar # => false
pp foo.same? bar3.foo # => true
 
bar4 = bar.clone
 
pp bar4.same? bar # => false
pp bar4.foo.same? foo # => false
1 Like

With clone now I can solve the problem !!! Thanks a lot man.

I will be more attentive now for that

1 Like