public int build() throws IOException {
    if (Files.exists(file)) {
      Files.delete(file);
    }

    if (!Files.isDirectory(file.getParent())) {
      Files.createDirectories(file.getParent());
    }

    // Sort the file infos and add the directories
    Set<FileInfo> fileInfos = new TreeSet<>();
    for (FileSet fileSet : fileSets) {
      Set<Directory> dirs = fileSet.toDirectories();
      dirs.removeAll(directories);
      for (Directory dir : dirs) {
        directories.add(dir);
      }

      fileInfos.addAll(fileSet.toFileInfos());
    }

    int count = 0;

    try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(file))) {
      for (Directory directory : directories) {
        String name = directory.name;
        ZipEntry entry = new ZipEntry(name.endsWith("/") ? name : name + "/");
        if (directory.mode != null) {
          entry.setUnixMode(FileTools.toMode(directory.mode));
        }
        zos.putNextEntry(entry);
        zos.closeEntry();
        count++;
      }

      for (FileInfo fileInfo : fileInfos) {
        ZipEntry entry = new ZipEntry(fileInfo.relative.toString());
        entry.setLastAccessTime(fileInfo.lastAccessTime);
        entry.setLastModifiedTime(fileInfo.lastModifiedTime);
        entry.setTime(fileInfo.lastModifiedTime.toMillis());
        entry.setSize(fileInfo.size);
        entry.setUnixMode(fileInfo.toMode());
        zos.putNextEntry(entry);
        Files.copy(fileInfo.origin, zos);
        zos.flush();
        zos.closeEntry();
        count++;
      }
    }

    return count;
  }